Как написать смарт-контракт для ICO за 5 минут



    Всем привет! В этой статье я расскажу вам, как за 5 минут и несколько команд в терминале запустить смарт-контракт сбора денег для своего ICO на Ethereum. Этот очерк потенциально сэкономит вам десятки тысяч американских долларов, так как любой программист — да и не программист тоже — сможет запустить проаудированный и безопасный смарт-контракт (вместо того, чтобы платить $15,000 – $75,000 за разработку). Вкратце, на этот смарт-контракт можно будет отправить денег и получить за это ERC20 токены. Можно сказать, эта статья — сборник всего опыта, который я получил, запуская ICO для своего проекта.

    В Интернетах этих ваших и так полно статьей про смарт-контракты, но как только начинаешь писать оный, сталкиваешься с тем, что информация везде повторяется, а туториалов, как запулить свой ERC20 попросту либо нет, либо они устарели что аж донельзя. К слову, чтобы эта статья оставалась актуальной, постараюсь указать потенциальные места, где она может устареть (и как это поправить). Поехали!

    Solidity


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

    Смарт-контракты


    … и все, что вам нужно о них знать. Пропустите эту секцию, если вы не программист. Смарт-контракт — это кусок кода. В принципе, это класс в солидити (ООП, да), у которого есть два типа функций: изменяющие состояние и не изменяющие. Ну и чтобы запускать функции в смарт-контракте просто отправив на него кефир, нужно эту функции пометить payable.

    Состояние — это хранилище данных, блокчейн, епта. Контракты могут изменять блокчейн (состояние, хранилище) — но чтобы изменить блокчейн нужно заплатить кефира майнерам. Как они будут делить кефир разбирать не будем в рамках этой статьи. Оплата майнерам за запуск кода, изменяющий состояние, называется Газом (Gas). Если кто-то извне закинет кефира на адрес смарт-контракта с вызовом функции, помеченной payable, но не помеченной Constant, View или Pure, то из отправленной суммы будет вычтено нужное количество кефира для оплаты майнерам. Обычно в ERC20 токенах это функции, которые либо выдают отправителю токенов за кефир, либо переводят токены от одного держателя токенов другому.

    А если вы пометите функцию в контракте словами Constant или View (означают одно и то же, разрешают только читать состояние), либо Pure (то же самое, только даже состояние не читает), то на исполнение этой функции даже кефир тратить не нужно будет! Даже больше скажу, эти функции не нужно вызывать транзакциями — ведь любой клиент кефира, теоретически, сможет ее выполнить у себя — и никому больше об этом знать не нужно (в блокчейн ведь ничего не пишется).

    А еще есть две важные штуки в солидити: множественное наследование и модификаторы функций. Про них тоже нужно знать.

    Первое — просто контракты могут наследоваться одновременно с нескольких классов типа TimedCrowdsale, CappedCrowdsale, MintedCrowdsale, Ownable — при этом функции конструкторов тоже запускаются друг за другом — но это я объясню на примере уже дальше.

    Второе — это возможности создавать функции, которые потом будут вставлены в другие функции. Это как простая инкапсуляция, только чуть более гибкая — это буквально шаблон функции. Когда вы создаете модификатор, вы пишете специальный символ _ там, где подразумеваете код функции, использующей этот модификатор. То есть модификаторы — это не просто инкапсулированный функционал, который возвращает значение; это — шаблон функции, когда код из модификатора буквально вставляется в функцию, использующую этот модификатор.

    Перейдем к практике.

    Готовим окружение


    Если вы не знаете, что такое Терминал — почитайте вот эту статью. Если вы на окнах, ставьте себе Терминал через WLS. Если вы уже знакомы с Терминалом, продолжим. Алсо, сразу поставьте себе Node.js — он будет необходим для следующих шагов. Лучше ставить LTS, но, на самом деле, абсолютно без разницы, какую из современных версий ноды ставить.

    Первое, что мы сразу поставим и запустим процесс синхронизации блоков — это geth. Короче, это утилита, написанная на Go, которая позволит нам запускать ноду эфира на локальном компе и коннектиться к тестовой и реальной сети. Установить можно через инсталяторы, но я крайне советую фигачить geth сразу в Терминал, как описано вот тут. Проверить, установился ли у вас норм geth, можно, запустив команду в Терминале:

    geth version

    Если вам выплюнуло версию geth — все в ажуре, продолжаем туториал. Если нет — хреново, исправляйте; придется, похоже, заняться любовными ласками с Терминалом и своей операционной системой — но вам не впервой, разберетесь. Как установите geth, запускайте в Терминале команду:

    geth --testnet console

    Это запустит процесс синхронизации вашей ноды с тестовым сервером, блоки которого можно глянуть вот тут. Проверить, синхронизировались ли вы с сетью в консоли geth можно, прописав:

    eth.blockNumber # если 0 — то еще не синхронизировались
    eth.syncing # выплюнет прогресс синхронизации или false, если ничего не происходит

    Процесс синхронизации у меня занимал от 1 до 4 часов — когда как. Алсо, помимо синхронизации блоков, придется ждать еще и синхронизации состояний — это чаще дольше, чем синхронизация блоков. Также можно использовать команды geth с флагом --light — тогда синхронизация длится от нескольких секунд до минуты и вы все еще можете деплоить контракты.

    Ладно, первую утилиту мы поставили — ставим следующую. Нам нужно поставить аналог geth, только совсем уж локальную симуляцию блокчейна — testrpc. Да-да, у нас 3 блокчейна:

    • testrpc — локальная симуляция блокчейна; быстрая, но ненастоящяя и хранится только у вас на машине
    • geth --testnet — уже реальный блокчейн, но тестовая сеть, где можно бесплатно получать кефир и тестить всякие непотребства, денег не потеряете
    • geth — мейннет, главный, реальный блокчейн, настоящий кефир; все по-взрослому, ошибки тут — потери реального кефира

    Соответственно, начнем мы тест контрактов с testrpc, потом задеплоим в geth --testnet, а потом зафигачим прямо в geth.

    Ставим testrpc, запустив следующую команду:

    npm install -g ethereumjs-testrpc

    Ну или встанет сразу с трюфелем, так как теперь testrpc под крылом трюфеля и зовется ganache-cli. Хотя черт его знает, у меня все и с ванильным testrpc сработало. А если работает — не трогай, как учили меня в межгалактической академии. Можно еще его и запустить, чтобы проверить установку, прописав truffle в консоли, но у нас уже синхронизируется тестовый блокчейн — не будем ему мешать.

    Ну что, разобрались с блокчейнами? Ноды теперь есть и тестовая даже синхронизируется? Ставим удобную утилиту для работы со смарт-контрактами на кефире — truffle, следующей командой:

    npm install -g truffle
    truffle version # сразу чекнем, установился ли, проверив версию

    Трюфель — это тулза, которая позволяет держать смарт-контракты в разных файлах, импортировать другие файлы, а так же компилирует ваш код смарт-контрактов в один большой байт-код (нечитаемый человеком), автоматически находит у вас локально запущенный geth (тестовый и реальный) или testrpc, деплоит ваш смарт-контракт в эту сеть. Алсо, проверяет ваш код смарт-контракта на ошибки и с недавних пор еще и дебажить помогает завершенные транзакции. Мастхев, короче.

    На этом этапе у вас должны быть установлены: testrpc, geth, truffle — если чего-то из этого нет или версия не выплевывается в консоль по запросу, то поправьте это; иначе не получится у вас ничего.

    Алсо, я накидал простенький баш-скриптик, который установит все за вас. Вызывается вот так:

    source <(curl -s https://raw.githubusercontent.com/backmeupplz/eth-installer/master/install.sh)

    — но я его ни разу не тестил еще, так что не уверен в его работоспособности. Однако пулл-реквестам буду только рад.

    Фигачим контракт


    За вас все уже придумано и написано — это хорошо. Немного головняка будет все равно — но я постараюсь вам его минимизировать. Использовать мы будем уже готовые ERC20 контракты от OpenZeppelin — это сейчас стандарт индустрии, они прошли аудит, да и вообще все их код используют. Спасибо им огромное за вклад в опенсоус.

    Сделайте cd в какую-нибудь безопасную папку и после пропишите:

    mkdir contract && cd contract

    В этой папке и будем работать. Создадим здесь заглушку для нашего смарт-контракта:

    truffle init

    Зашибись, четко. У нас теперь есть две очень важные папки, в которые мы и будем лезть: contracts и migrations. Первая — код наших контрактов, вторая — код для truffle, чтобы знать, что делать при деплое контрактов в блокчейн.

    Дальше нам нужно забрать текущий код смарт-контрактов из npm и, собственно говоря, начать сам проект:

    npm init -y # создадим проект без вопросов (флаг -y)
    npm install -E openzeppelin-solidity # заберем контракты и зафиксируем текущую версию (флаг -E)

    Отлично, код смарт-контрактов от OpenZeppelin у нас в кармане в папке node_modules/openzeppelin-solidity/contracts. Теперь заходим в главную папку contracts, удаляем там все файлы и добавляем файлы MyToken.sol и MyCrowdsale.sol — естественно, свои контракты вы назовете иначе. Первый будет контрактом на наш ERC20 Токен, а второй — контрактом нашего ICO, который будет принимать кефир и раздавать людям MyToken. Эта статья может устареть, но вы всегда можете глянуть, как OpenZeppelin предлагают вам создавать контракты у них в репозитории. Вот так у нас будет выглядеть MyToken.sol:

    pragma solidity ^0.4.23;
    
    // Imports
    import "../node_modules/openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol";
    
    // Main token smart contract
    contract MyToken is MintableToken {
      string public constant name = "My Token";
      string public constant symbol = "MTKN";
      uint8 public constant decimals = 18;
    }

    Найс — у вас есть смарт-контракт собственного токена (только смените названия в константах)! Можете глянуть, что там за наследование от MintableToken — но там все максимально просто. Это токен, который можно выпускать (от англ. «Mint» — чеканить), и выпускать его имеет право только владелец, так как MintableToken еще и наследуется от Ownable. Алсо, MintableToken еще и наследуется от классов ERC20 токенов, написанных OpenZeppelin, в которых и реализован интерфейс ERC20:

    contract ERC20Basic {
      function totalSupply() public view returns (uint256);
      function balanceOf(address who) public view returns (uint256);
      function transfer(address to, uint256 value) public returns (bool);
      event Transfer(address indexed from, address indexed to, uint256 value);
    }

    Ага, вот вам и весь ERC20 интерфейс. Сложно? Не думаю. Дает возможность глянуть, сколько было выпущено токенов, проверить баланс адреса и перевести токенов на другой адрес, выплюнув в сеть событие перевода для легких клиентов кефира. И все это вы получаете фор фри в вашем MyToken.sol благодаря работе OpenZeppelin — они молодцы.

    А теперь перейдем к главной части нашего ICO — нам же нужно принимать кефиры и раздавать MyToken! Вот так будет выглядеть ваш MyCrowdsale.sol:

    pragma solidity ^0.4.23;
    
    // Imports
    import "../node_modules/openzeppelin-solidity/contracts/crowdsale/emission/MintedCrowdsale.sol";
    import "../node_modules/openzeppelin-solidity/contracts/crowdsale/distribution/RefundableCrowdsale.sol";
    import "../node_modules/openzeppelin-solidity/contracts/crowdsale/validation/CappedCrowdsale.sol";
    import "../node_modules/openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol";
    
    contract MyCrowdsale is CappedCrowdsale, RefundableCrowdsale, MintedCrowdsale {
     constructor(
      uint256 _openingTime,
      uint256 _closingTime,
      uint256 _rate,
      address _wallet,
      uint256 _cap,
      MintableToken _token,
      uint256 _goal
     )
      public
      Crowdsale(_rate, _wallet, _token)
      CappedCrowdsale(_cap)
      TimedCrowdsale(_openingTime, _closingTime)
      RefundableCrowdsale(_goal)
     {
      // Это тут просто, чтобы показать, что можно сделать
      // Проверяем, что софткеп ниже хардкепа
      require(_goal <= _cap);
     }
    }
    

    Так-так-так, что тут у нас? Что, пацаны, смарт-контракты? Наша публичная продажа токенов наследует три самых популярных свойства: у нее есть хард-кап, больше которого собрать не получится; софт-кап, не собрав который эфиры возвращаются; время начало и конца продажи токенов. Собственно говоря, а что еще для счастья нужно?

    Программисты, обратите внимание, как конструкторы классов множественного наследования выстроены в ряд и получают аргументы из главного конструктора MyCrowdsale. Алсо, мы проверяем, что хардкеп у нас выше софткепа — алес гут! Алсо, не пугайтесь туче параметров в конструкторе MyCrowdsale — мы передадим их на этапе деплоя контракта в трюфеле.

    Вот и все — у вас есть готовые контракты вашего собственного ERC20 токена и даже смарт-контракт ICO, который настраивается по вашему желанию и раздает ваши токены за кефир. Алсо, его поддерживают все ERC20 кошельки — ляпота! Перейдем к ручным тестам и деплою.

    Миграции


    Как я уже говорил ранее, тестировать мы будем последовательно на трех блокчейн-сетях, но процесс тестирования ручками всегда будет один и тот же. Начнем с testrpc, потом перейдем к geth --testnet и дальше в geth. Соу фар мы только писали код, давайте его попробуем скомпилировать. В папке проекта пропишите:

    truffle compile

    Если все скомпилировалось без проблем — то у вас появится папочка build, в которой будет содержаться кракозябра для трюфеля, чтобы он смог задеплоить в блокчейн байт-код ваших смарт-контрактов. Перед тем, как деплоить смарт-контракты, нам нужно рассказать трюфелю, что вообще нужно делать. Деплой смарт-контрактов в трюфеле называется миграцией — ну, что же, будем придерживаться этой терминологии. Зайдите в migrations/1_initial_migration.js и измените его следующим способом:

    const token = artifacts.require("../contracts/MyToken.sol");
    const crowdsale = artifacts.require("../contracts/MyCrowdsale.sol");
    
    module.exports = function(deployer, network, accounts) {
        const openingTime = 1514764800; // 15 Июня 2018
        const closingTime = 1561939200; // 1 Июля 2019
        const rate = new web3.BigNumber(1); // 1 токен за 1 эфир
        const wallet = '0x281055afc982d96fab65b3a49cac8b878184cb16'; // Кошелек-бенефициар
        const cap = 200 * 1000000; // Хардкеп
        const goal = 100 * 1000000; // Софткеп
    
        return deployer
            .then(() => {
                return deployer.deploy(token);
            })
            .then(() => {
                return deployer.deploy(
                    crowdsale,
                    openingTime,
                    closingTime,
                    rate,
                    wallet,
                    cap,
                    token.address,
                    goal
                );
            })
            .then(() => {
                // Crowdsale должен владеть токеном
                var tokenContract = web3.eth.contract(token.abi).at(token.address);
                web3.eth.defaultAccount = web3.eth.accounts[0];
                tokenContract.transferOwnership(crowdsale.address);
            });
    };

    Это тот самый файл, который будет использоваться трюфелем для деплоя контрактов. Что же мы тут такого наворотили? Во-первых, мы запросили скомпилированные MyToken и MyCrowdsale. После, мы установили константы со всеми аргументами нашего ICO — установили время начала и конца; сколько токенов будут получать люди за 1 вей кефира (0.000000000000000001 eth = 1 wei; установка decimals указывает, сколько нужно порядков wei, чтобы получить 1 ваш новоиспеченный токен); кошелек, куда придут полученные на продаже кефиры; хард-кеп и софт-кеп. Учтите, что openingTime всегда должен быть после времени текущего блока в блокчейне — иначе ваш смарт-контракт не задеплоится из-за проверки условия в TimedCrowdsale. Я на эти грабли наступал, а провалившиеся транзакции вообще никак не получается дебажить. Меняйте эти константы по своему усмотрению.

    Следующий шаг — это именно деплой смарт-контрактов. Тут ничего интересного: у нас есть объект deployer, который деплоит артефакты смарт-контрактов и передает туда аргументы. Заметьте, что сначала деплоится MyToken, и только потом MyCrowdsale — и во второй передается аргументом адрес первого.

    Дальше самое интересное — то, о чем не пишут ни в документации, ни в книжках. Когда вы создаете с кошелька MyToken, этот кошелек становится владельцем MyToken по суперклассу Ownable — то же самое происходит и с MyCrowdsale. Если глубоко копнуть в MintableToken, то можно увидеть, что чеканить монеты-то может только Owner! А кто владелец MyToken? Правильно: адрес, который его и задеплоил. А кто будет отправлять запросы на чеканку монет? Правильно: смарт-контракт MyCrowdsale. Напомню, что адрес, создавший MyToken и адрес MyCrowdsale — это два разных адреса.

    Поэтому у нас добавляется неправославный третий шаг деплоя, где адрес, задеплоивший контракты (web3.eth.accounts[0]) вызывает функцию transferOwnership на контракте MyToken, чтобы MyCrowdsale владел MyToken и мог чеканить монеты. А MyCrowdsale все еще под владением web3.eth.accounts[0] — так что все пучком.

    Заметка про web3.eth.accounts[0]: когда деплоите смарт-контракт, убедитесь, что geth или testrpc имеют правильный кошелек в web3.eth.accounts[0] — не теряйте приватный ключ к нему, хоть это никак вам не навредит, но вдруг владельцу что-нибудь потом нужно будет сделать, а ключа уже нет?
    В testrpc, как правило, аккаунты создаются сразу при запуске и они сразу же разлочиваются; однако на тестовом и реальном блокчейне эфира стоит создать аккаунт через personal.newAccount() — дальше пополнить этот адрес через Faucet на тестовом блокчейне или реальным кефиром на реальном блокчейне. Не теряйте пароль и приватные ключи.
    Алсо, вы можете в аккаунты добавить уже существующий кошелек, вызвав web3.personal.importRawKey('pvt_key', 'password'), но для этого нужно вызывать geth с дополнительным параметром --rpcapi="db,eth,net,web3,personal,web3". Думаю, разберетесь.

    Тестирование и деплой


    Йес, контракты готовы, миграции написаны, осталось только задеплоить и проверить. Как geth (тестовый и реальный), так и testrpc управляются одинаково через truffle console — так что опишу способ проверки для testrpc и просто расскажу, как включить geth после. И так, запускаем тестовый локальный блокчейн кефира:

    testrpc

    Эм… вот и все. У вас локально работает симуляция блокчейна кефира.

    А чтобы задеплоить в тестовый блокчейн эфира, вы вместо этой команды сделаете geth --testnet --rpc. А чтобы задеплоить в реальный блокчейн эфира, вы пропишите просто geth --rpc. Флаг --rpc нужен, чтобы трюфель смог подключиться. Следующие шаги деплоя и теста более-менее одинаковы для всех трех типов блокчейна. Единственное что — после того, как вы запустите тестовый или реальный блокчейн через geth, он начнет синхронизировать блоки — а это может занять до 4-5 часов на хорошем Интернет-соединении. Ремарка про эта была в самом начале статьи. Перед деплоем смарт-контрактов рекомендую дождаться полной синхронизации. Алсо, блокчейн весит в районе 60-100 гигабайт, так что подготовьте для этого место на диске.
    Алсо-алсо, убедитесь, что web3.eth.accounts[0] разлочен. Обычно можно прописать в консоли testrpc, которая открывается сразу, либо в отдельном окошке Терминала в консоли, которая открывается через geth console: eth.unlockAccount(eth.accounts[0], "Пароль, полученный при создании учетки", 24*3600) — это разлочит ваш аккаунт, который должен создать смарт-контракт

    Теперь открываем новое окошко Терминала (testrpc не закрываем — он должен работать) и прописываем в папке проекта:

    truffle migrate --reset

    Эта магическая команда скомпилирует смарт-контракт (то есть не нужно каждый раз писать truffle compile) и задеплоит его на микро-сервер блокчейна, найденный открытым локально. Стоит отметить, что если testrpc сделает это мгновенно, то тестовый и реальный блокчейны будут гораздо дольше включать транзакцию в следующие блоки. После этого у вас должно выплюнуться нечто подобное в консольку:

    Using network 'development'.
    
    Running migration: 1_initial_migration.js
     Running step...
     Replacing MyToken...
     ... 0x86a7090b0a279f8befc95b38fa8bee6918df30928dda0a3c48416454e2082b65
     MyToken: 0x2dc35f255e56f06bd2935f5a49a0033548d85477
     Replacing MyCrowdsale...
     ... 0xf0aab5d550f363478ac426dc2aff570302a576282c6c2c4e91205a7a3dea5d72
     MyCrowdsale: 0xaac611907f12d5ebe89648d6459c1c81eca78151
     ... 0x459303aa0b79be2dc2c8041dd48493f2d0e109fac19588f50c0ac664f34c7e30
    Saving artifacts...

    Думаю, вы уже поняли, что консолька вам выдала адреса смарт-контрактов MyToken и MyCrowdsale. Все! Смарт-контракт задеплоен в тот блокчейн, микро-сервер которого у вас открыт. Осталось лишь проверить, что токены и вправду раздаются юзерам, которые присылают кефир на смарт-контракт MyCrowdsale. Прописываем в Терминале следующее, чтобы зайти в консоль трюфеля:

    truffle console

    Прописываем следующее в теперь уже трюфеле (без комментариев только):

    // Сохраняем адреса смарт-контрактов
    t="0x2dc35f255e56f06bd2935f5a49a0033548d85477" // Замените на адрес своего MyToken
    с="0xaac611907f12d5ebe89648d6459c1c81eca78151" // Замените на адрес своего MyCrowdsale
    
    // Получаем инстансы смарт-контрактов
    token=MyToken.at(t)
    crowdsale=MyCrowdsale.at(c)
    
    // Сохраним аккаунт в более короткое имя
    account=web3.eth.accounts[0]
    
    // Проверяем, сколько токенов у нашего аккаунта
    token.balanceOf(account) // должно быть 0
    
    // Отправляем кефира на смарт-контракт
    web3.eth.sendTransaction({from: account, to:c, value: web3.toWei(0.1, 'ether'), gas: 900000})

    В случае с testrpc можно сразу же проверять снова баланс нашего кошелька, но в случае с тестовым и реальным блокчейном нужно подождать, пока транзакция наша будет включена в блок — обычно, когда это происходит, трюфель выдает вам номер транзакции. Подождали? Проверяем снова наш баланс в MyToken:

    // Проверяем, сколько токенов у нашего аккаунта
    token.balanceOf(account) // должно быть больше нуля

    Вот и все! Сначала тестите свой контракт на testrpc, потом на geth --testnet, потом деплойте на geth. Вот и запустили вы свое собственное ICO! И не пришлось вам тратить десятки килобаксов на аудит и запуск. Накосячить с тем, что нам предоставили ребята из OpenZeppelin, на самом деле, очень сложно. А когда вы используете truffle — так разработка на солидити вообще в сказку превращается. Ну, кроме случаев, когда транзакции ревертятся еще во время выполнения на смарт-контракте — дебажить их сущий ад. Но дебаггинг смарт-контрактов, воистину, достоин отдельной статьи.

    Заключение


    Огромное спасибо, что дочитали до конца этой статьи! Если мне удалось сэкономить вам время или деньги, либо если вы узнали что-то новое из этой статьи, то я этому буду очень рад. Буду так же очень признателен, если поделитесь этой статьей со своими друзьями или знакомыми, которые хотят провести ICO — сэкономьте им $75,000 на недо-программистов, которые высасывают деньги из крипто-рынка, как паразиты, копи-пастя одни и те же 25 строк кода.

    Удачи в разработке смарт-контрактов! Остались вопросы? Милости прошу в комментарии — с удовольствием на все отвечу и постараюсь помочь с проблемами.

    Бонус


    А что, если вы хотите изменить логику, по которой считается цена покупки токенов? Конечно, можно изменить правильно rate или использовать один из классов контрактов от OpenZeppelin, но вдруг вы хотите чего-нибудь еще более извращенного? В смарт-контракте можно оверрайтнуть функцию getTokenAmount следующим образом:

    function _getTokenAmount(uint256 _weiAmount)
        internal view returns (uint256)
      {
        if (block.timestamp < 1533081600) { // August 1st, 2018
          rate = rate * 4;
        } else if (block.timestamp < 1546300800) { // January 1st, 2019
          rate = rate * 2;
        }
        return _weiAmount.mul(rate);
      }

    В общем, так можно сделать цену токена зависящей от времени покупки — чем дальше в лес, тем дороже токены. Не бойтесь экспериментировать и переписывать некоторые функции смарт-контрактов — это весело!

    Поделиться публикацией
    Похожие публикации
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 46
      +7
      Критику принимаете?
      Ну тогда вот.
      • Поменьше жаргона
      • Расшифровать обозначения и малоупотребимые слова. В том числе и жаргон, если его нельзя избежать
      • Строго говоря за 5 минут не поднять этот смарт-контракт (кстати что это?). Я статью читал дольше
      • Язык Solidity может и простой, но пока во всей терминологии разберешься — потратишь кучу времени

      И вот как пример — это можно на русский язык перевести?
      Алсо, мы проверяем, что хардкеп у нас выше софткепа — алес гут! Алсо, не пугайтесь туче параметров в конструкторе MyCrowdsale — мы передадим их на этапе деплоя контракта в трюфеле.
        +1
        1. Сорямба за жаргон, мне больше важно наполнение, нежели форма
        2. Да все достаточно понятно — это просто мой художественный стиль, на самом деле
        3. Нет, поднять за 5 минут после прочтения вполне можно — там пара-тройка команд в консоли (а про то, что такое смарт-контракт, написаны десятки статей)
        4. Да нет, не особо-то и кучу времени — большинство вещей в Солидити заимствовано из других ЯП

        В общем, спасибо за критику — но, к сожалению, это мой художественный стиль раздолбайский :( постараюсь в будущем исправиться!
        Алсо, мы проверяем, что хардкеп у нас выше софткепа — алес гут! Алсо, не пугайтесь туче параметров в конструкторе MyCrowdsale — мы передадим их на этапе деплоя контракта в трюфеле.
        • «Алсо» — от английского «also», в переводе означает примерно «также»
        • «Хардкеп» и «софткеп» — устоявшиеся в сфере ICO термины границ сбора средств
        • «Алес гут» — от немецкого «alles gut», в переводе — «все хорошо»
        • «Конструктор» — это функция создания объекта соответственного класса
        • «Деплой» — от английского «deploy» — запуск написанного кода, часто в рабочее окружение пользовательской среды
        • «Трюфель» — это англицизм от названия командной программы «truffle»

        Черт, меня не было полтора года на Хабре. Куда делись хардкорные разработчики, которые ценят подобный «раздолбайский» художественный стиль редактуры?
          +2
          Алсо, мы проверяем, что хардкеп у нас выше софткепа — алес гут! Алсо, не пугайтесь туче параметров в конструкторе MyCrowdsale — мы передадим их на этапе деплоя контракта в трюфеле.

          Итого:
          Также, мы проверяем, что жесткая граница сбора средств (hard-cap) у нас выше вариабельной границы сбора средств (soft-cap) — всё хорошо. Также, не пугайтесь большому количеству параметров в конструкторе MyCrowdsale — мы передадим их на этапе запуска кода контракта в командной программе truffle

          Всё так?

          P.S.: Простите мое занудство, написано интересно, но жаргон сбивает с толку
            0
            Все примерно так, да! Спасибо за расшифровку. В следующих статьях обязательно исправлюсь и начну использовать жаргон меньше — я новенький на Хабре, еще не освоился.
              +1
              Ну я бы не сказал что новенький :) Скорее я такой. Но вот вашу предыдущую статью — вполне приятно читать, несмотря на жаргон — он там есть, но его немного и не касается специфичных терминов.
                0
                Похоже, в какой-то момент жизни у меня произошла деформация речевого аппарата, что повлекло за собой жаргонирование письменных текстов. Ну, буду бороться с этим. В любом случае, благодарю за критику! Учту обязательно.
          +3
          Да, статью читать не приятно с таким жаргоном.
            0
            Хм, а наполнение вы как оцените по шкале от 1 до 10? Где 1 — сообществу абсолютно не нужно (очередной абстрактный очерк о смарт-контрактах в общем), и 10 — где статья, необходимая развитию отрасли (документация Solidity).
          +1
          Почему не ERC-223?
            0
            Потому же, почему не ERC721 и не ERC827 — нужно же с чего-то начинать. Может, следующей частью рассмотрю разные стандарты и как их запустить — там все достаточно одинаково.
              +2
              ERC721 не является заменой ERC-20, а ERC-223 исправляет пару проблем ERC-20 и обратно совместим. Он лучше во всём. И, вроде бы, не намного и сложнее. Могу ошибаться.

              Просто забавно периодически читать, что скоро осуществится глобальный переход на ERC-223, и видеть всё новые токены на уже «устаревшем стандарте»)
                0
                Ну, насколько мне известно, ERC-223 это лишь токен с «защитой от дурака» — чтобы нельзя было перевести токены на какой-нибудь смарт-контракт без возможности вывода :) но это уже совсем частные детали. Согласен, что стоит переходить на убер ERC-223, похоже, точно стоит об этом статью начеркать. Спасибо!
                  +1
                  И где вы это читаете? Про глобальный переход

                  223 даже тут нет eips.ethereum.org/all, т.е. его статус не определено вообще
              0
              Ждём статью «Как написать бэк-енд для магазина за 5 минут» или «Разворачиваем AWS окружение за 5 минут»
                +1
                Ну а что, не стоило писать эту статью? :( Я, когда пытался запустить свой смарт-контракт, так и не нашел нормальной последовательной статьи о том, как это сделать. Ну и подумал, вдруг кому полезно будет, если такая статья окажется в Интернете :( можете поделиться другой актуальной статьей на тему запуска смарт-контракта ICO, пожалуйста?
                  0
                  Таких статей скорее всего нету потому что у каждого свои требования к контрактам. А такие вот «за 5 минут» нередко потом выходят боком, который к тому же не всегда можно пофиксить)
                    +2
                    Гхм. Если таких статей нет — то как начать писать типовые смарт-контракты под ICO? И чем эти требования уникальны? В последнее время что не ICO — так Timed, Capped, Refundable, etc. Все это уже есть в OpenZeppelin, а расширяется смарт-контракт максимально просто (в конце статьи пример привел).

                    Так же в статье я описал способы тестировать смарт-контракты на тестовых блокчейнах. Знаете, что чаще всего выходит боком? Когда разработчики вместо того, чтобы взять проаудированный OpenZeppelin начинают фигачить свой велосипед с нуля. Вот тогда — беда.
                      0
                      Ну и зеплин не всегда сахар)
                      github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/token/ERC20/BasicToken.sol
                      Основной метод, transfer (31 строка):
                      function transfer(address _to, uint256 _value) public returns (bool) {
                      require(_to != address(0));
                      require(_value <= balances[msg.sender]);

                      balances[msg.sender] = balances[msg.sender].sub(_value);
                      balances[_to] = balances[_to].add(_value);
                      emit Transfer(msg.sender, _to, _value);
                      return true;
                      }


                      Не находите ничего странного?
                      sub в данном случае не нужен, так как проверка на необходимое значение балансу уже описано в require(_value <= balances[msg.sender]);
                      скажете неважный момент, представьте сколько таких контрактов и сколько было этих transfer и сколько газа система потратила на эту проверку)

                        0
                        Эм, простите, но 35 строка (balances[msg.sender] = balances[msg.sender].sub(_value);) — это не какая-то проверка, это изменение количества токенов на кошельке отправителя. Так что, честно скажу, не понимаю к чему вы тут придираетесь.

                        Если убрать 35 строку — то при переводе баланс отправителя не будет меняться и можно будет бесконечно создавать токены. Если убрать 33 строку (require(_value <= balances[msg.sender]);), то есть вероятность, что баланс отправителя уйдет в минус, а получатель получит токенов больше, чем было у отправителя.

                        Так в чем претензия? Ну, еще учтите, что компилятор от truffle тоже оптимизации какие-никакие, но делает.
                          0
                          Строку не нужно убирать, а следует писать:
                          balances[msg.sender] = balances[msg.sender] - _value;
                          а в sub происходит еще одна дополнительная проверка которая в нашем случае лишняя.
                            0
                            Неправильно вы пулл-реквесты пишете, товарищ — нужно их делать на ГитХабе, а не на Хабре. Вот, засабмитил PR — спасибо, благодаря вам код станет лучше.

                            Линуксом тоже перестанем пользоваться из-за того, что он в некоторых местах недостаточно оптимизирован? Может и C++ дропнем по той же причине? А там и С не особо-то и вежливо пользуется регистрами процессора. Вот и будем жить с Assembly или машинным кодом. Вообще, идеал — перфокарты.

                            Никто не говорил, что OpenZeppelin идеален — но мы, как сообщество open-source, стараемся делать его лучше с каждым днем. И это далеко не аргумент против использования этого готового кода.
                              0
                              Это разные проверки для разных вещей.

                              Тут require для пользовательского ввода (возвращает неиспользуемый газ)
                              Там assert для внутренних операций (логическая ошибка, жрет весь газ)

                              Находясь в функции вы не должны знать, как там работает внутри sub из safemath. Вы просто его используете, т.к. он безопасен (underflow).

                              Т.к. ZS — это примеры безопасного кода, а не оптимизированного, то я удивлюсь, если они примут этот PR

                              kozyabka — для привлечения внимания :)
                                0
                                И в каком случае в это месте может произойти underflow?
                                ПС. проверка эта избыточна)
                                  0
                                  О! Вы вернулись. Как так получилось, что вы больше времени потратили на написание комментария на Хабре, когда могли бы гораздо меньше времени потратить на PR?
                            0
                            Так что, честно скажу, не понимаю к чему вы тут придираетесь.

                            Вот Вам пример 5и минутных обучений ;)
                              0
                              Предложите альтернативу OpenZeppelin? :) Или каждому ICO переизобретать колесо снова и снова, делая одни и те же ошибки и наступая на одни и те же грабли?
                                0
                                Дело в другом. Писать смарт контракты за «5 минут» без реального понимания кода, эфира и вот этого всего это не супер идея. Меня неоднократно просили разруливать проблемы с контрактами, и не всегда это вообще возможно. Лучше доверять профессиональным инженерам или самому разобраться на хорошем уровне. Если суммы серъёзные планируете собрать и плюс ко всему это утилити токены.
                                  0
                                  Никто и не советует в статье писать смарт-контракты «без реального понимания кода, эфира и вот этого всего» — наоборот, в статье я неоднократно призываю именно углубиться в реализацию контрактов, разобраться в них сильнее.

                                  И да, закосячить смарт-контракт на основе OpenZeppelin и этой статьи — это нужно будет постараться. Эта статья, скорее, не призыв «делайте смарт-контракты за 5 минут», а просто пруф концепта: «вот так можно сделать смарт-контракт за 5 минут», объясняющий основные части разработки и запуска смарт-контракта.
                  +1
                  Буду так же очень признателен, если поделитесь этой статьей со своими друзьями или знакомыми, которые хотят провести ICO — сэкономьте им $75,000 на недо-программистов, которые высасывают деньги из крипто-рынка, как паразиты, копи-пастя одни и те же 25 строк кода.

                  Некоторые ICO имеют специфичные условия продажи токенов, например, включают процедуру их сжигания; а где-то требуется заморозка токенов на определенный период. Подобные нюансы каждой кампании по сбору средств, видимо, и вынуждают обращаться к тем самым «недопрограммистам», которые высасывают деньги из крипто-рынка. Хотя согласен, что цена их услуг совершенно неподъемная для маленьких стартапов.
                    0
                    Но и сжигаемость, и заморозка есть в контрактах OpenZeppelin! :) Так что эта статья — это просто указание на путь программистам и проектам, мол, не сложно все это. Да и даже с нуля дописать сжигаемость и заморозку на основе контрактов от OpenZeppelin — это строк 10-20 кода, с чем смогут справиться штатные программисты проекта. В общем, сколько ни говори об уникальности ICO — а они все похожи и все уже придумано и написано до них.
                      +1
                      Абсолютно с вами согласен. Порог вхождения, конечно, остается высоким, да и сама тема ICO у многих вызывает недоверие. Надеюсь, кто-нибудь уже наконец напишет «Конструктор ICO», чтобы не думать о коде и его последующем аудите. Вот тогда и цена на разработку смарт-контрактов упадет до среднерыночных и приемлемых $25-50 долларов в час.
                        0
                        Хе-хе-хе, это как-раз то, чем я сейчас занимаюсь: абсолютно бесплатный конструктор ICO с открытым исходным кодом — Fondu. Делаю пока что чисто ради фана — потом посмотрим, вдруг, людям понравится. Прямо там лежит и MVP — конструктор смарт-контрактов который я допилю завтра-послезавтра. Но монетизировать абсолютно никак не планирую: мое мнение — под каждый сбор денег на любую вещь можно создавать ICO, и именно этой мысли мы и придерживаемся.
                          0
                          Алсо, небольшой апдейт: теперь там можно собрать себе все нужные файлы смарт-контракта под ICO в конструкторе, скачать эти файлы и запустить небольшой скриптик, который задеплоит контракты в testrpc. Завтра, похоже, буду прикручивать `geth` :)
                            0
                            И еще один апдейт: теперь конструктор полностью рабочий. Указал все поля, скачал файлы, запустил ./deploy.sh и автоматически деплоишь смарт-контракты краудсейла в локальный, тестовый и главный блокчейны эфира.
                    +2
                    Так что абсолютно любой программист хотя бы уровня джуниора сможет разобраться в нем. Абсолютно нет смысла платить огромные деньги разработчикам, которые знают солидити — обучить уже существующего разработчика будет на порядок дешевле.

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

                      Ну и в этой статье мы рассмотрели именно ERC20, как базовый и самый распространенный способ работы с EVM. Что-либо сложнее — уже вне рамок статьи, что и является причиной, почему небольшое расширение упало в бонусную часть.
                        +1

                        Вы про какую реализацию EVM говорите? По долгу службы много копаюсь в Ethereum geth, недавно копался с багом с evm. И оно ни разу не для junior.

                          0
                          А, так и говорите: не «принципы и особенности работы EVM», а «внутреннее содержание кода EVM». Чтобы писать отличный код на Swift, не нужно быть экспертом в LLVM. Так же и с EVM. Чтобы писать отличный код на Solidity, не нужно знать все тонкости EVM.

                          С принципами и особенностями работы ознакомиться может любой джун, использовать их тоже. Никто не ведет речь про раскопки в коде EVM.
                            +1

                            Тогда ок. Просто насторожили слова "EVM изначально был написан максимально просто для разработчиков". Там в EVM не просто и немало мест с data race.

                              0
                              Понял вас, простите за то, что туго соображаю и плохо изъясняюсь. Я имел ввиду именно интерфейс взаимодействия с EVM, не само внутреннее строение EVM. Возвращаясь к теме треда, выскажу свое мнение: принципы работы блокчейна с особенностями EVM тоже вполне себе дешевле преподать существующим разработчикам, нежели искать спецефического блокчейн-девелопера.
                                +1

                                Согласен полностью.
                                И да, интерфейс EVM простой, как и его идея, зачем он нужен и как контракты исполняются.

                      0
                      Некоторые даже не заморачиваются с OpenZeppelin, просто идут сюда — www.ethereum.org/token потом «пишут» контракт методом CrtlC-CtrlV даже не вдаваясь в подробнсти того, чего скопировали и как это работает…

                        +1
                        Ни в коем случае не пропагандирую копи-пейст подход. Наоборот, в статье я не раз указывал, мол, сходите в суперклассы — посмотрите что и как там написано. OpenZeppelin большие молодцы — их код приятен для чтения и достаточно прост.

                        Кстати, работая с OpenZeppelin, гораздо проще понять, что делаешь, нежели с абстрактным примером из ethereum.org/token ;)
                          +2
                          Работу OpenZeppelin я оценил когда заинтересовался смарт-контрактами и соглашусь, что читать их намного приятней, чем абстактный код на ethereum.org. Как и соглашусь с тем, что иногда лучше скопипастить кусок кода, чем изобрести неведомую фигню которая непонятно каким боком может повернуться, особенно когда немного понимаешь, что копипастишь…
                          Что касается вашей статьи, то забавно было читать, такой смеси сленга-жаргона давно не встречал. Отметил про себя, что перевод сделанный там вверху, мне как-то туже было читать.
                        0
                        Огромное спасибо за статью!!! :)
                          0
                          Рад, что понравилось :) значит, писал не зря

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое