Создаем смарт-контракт для ICO. Часть 1 >

Создаем смарт-контракт для ICO. Часть 1

0
(0)

Как создать ICO

В этой статье я покажу как создать несложный смарт-контракт для проведения ICO, в часности для краудсейла (Crowdsale).

Что такое ICO? В переводе с английского — это «первичное размещение монет или токенов». Если говорить простыми словами, пользователь отсылает на адрес контракта эфиры, а взамен получает определенное количество токенов. В статье вы не прочтете о способах рекламы и раскрутки своего ICO, только техническая часть.

Итак, предположим что есть один или несколько человек, у них есть хорошая идея или даже продукт на начальной стадии готовности и они хотят ее реализовать. А для этого нужно финансирование. Они создают сайт с описанием своей идеи, создают Witepaper и для привлечения финансов решают провести ICO.

В мире классических финансов привлечение капитала выглядит немного по-другому. У вас есть идея или какой-то продукт, вам нужны финансы, то для их привлечения вам придется обивать пороги так называемых венчурных инвесторов.

Этапы ICO

В мире ICO есть свои этапы.

  • Pre-ICO — этот этап нужен для того, чтобы понять, нужен ли вообще кому-нибудь ваш токена и ваша идея. Например, вы решаете на Pre-ICO продать 30% всех ваших токенов по заниженной цене. Проведя этот этап вы понимаете, что покупателей мало, никому вы не нужны и нужно или сворачивать удочки, или менять свою идею, проводить дополнительную рекламу и так далее. А может быть вам повезет — все ваши токены купят, ваша идея всем нужна и может случиться даже так, что вы сразу привлечете к себе крупных инвесторов и отпадет необходимость проводить следующие этапы ICO.
    Также на этом этапе можно сделать некие ограничения, например, участвовать может только узкий круг лиц, или установить лимит на минимальную транзакцию, к примеру, не менее 5 ETH.
  • ICO — на этом этапе все пользователи без ограничений смогут купить ваши токены по обычной цене. Можно запрограммировать какие-то бонусы и скидки, партнерские программы и так далее.
  • Bounty / Post ICO — на этом этапе вы поощряете пользователей, которые рекламировали ваше ICO. Например, крупных юзеров на Bitcointalk, или в Твиттере и так далее.

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

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

Вариантов контракта для ICO может быть много. Ограничения по времени проведения, по эмиссии, резервирование части токенов команде разработчиков, условия достижения уровней Hard Cap и Soft Cap, замораживание купленных токенов до завершения ICO и другие.

Ниже я приведу код ICO-распродажи. Код рабочий, все токены приходят отправившим эфир пользователям, однако такой контракт в реальных проектах лучше не использовать. Как я уже писал, под каждый этап ICO создается свой контракт, а в нижеприведенном я все поместил в один: здесь и сам ERC20 токен, и функции распродажи.

Условия: контракт сразу создает 10 000 000 токенов, которые хранит на своем балансе. Продаваться токены будут за 0,001 ETH за штуку. То есть максимально выручить можно 10 000 эфиров, если продать все токены. Токен будет иметь стандартные 18 нулей после запятой.

Я взял код из первого урока по созданию токена в Эфире и переделал его. Токен будет называться CCR, производное от ContractCreator Token.



pragma solidity ^0.4.24;
 
contract contractCreator {
 
    uint256 totalSupply_; 
    string public constant name = "ContractCreator.ru Token";
    string public constant symbol = "CCT";
    uint8 public constant decimals = 18;
    uint256 public constant initialSupply = 10000000*(10**uint256(decimals));
    uint256 public buyPrice; //цена продажи
    address public owner; // адрес создателя токена, чтобы он мог снять эфиры с контракта
 
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
 
    mapping (address => uint256) balances; 
    mapping (address => mapping (address => uint256)) allowed;
    
    function totalSupply() public view returns (uint256){
        return totalSupply_;
    }
 
    function balanceOf(address _owner) public view returns (uint256){
        return balances[_owner];
    }
 
    function allowance(address _owner, address _spender) public view returns (uint256) {
        return allowed[_owner][_spender];
  }
 //--------------- Новое

	
	function _transfer(address _from, address _to, uint256 _value) internal returns (bool) {
        require(_to != address(0));
        require(balances[_from] >= _value); 
        balances[_from] = balances[_from] - _value; 
        balances[_to] = balances[_to] + _value; 
        emit Transfer(_from, _to, _value);
        return true;
    }
    
    function transfer(address _to, uint256 _value)  public returns (bool) {
        _transfer(msg.sender, _to, _value);
    }


		function _buy(address _from, uint256 _value) internal {
		uint256 _amount = (_value / buyPrice)*(10**uint256(decimals));
		_transfer(this, _from, _amount);
		emit Transfer(this, _from, _amount);
		}
		
		function() public payable{
			 _buy(msg.sender, msg.value);
		}
		
		function buy() public payable {
			_buy(msg.sender, msg.value);
		}
		

		function transferEthers() public {
			require(msg.sender == owner);
			owner.transfer(address(this).balance);
		}


//---------------------------------------------


 
    function approve(address _spender, uint256 _value) public returns (bool) {
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }
 
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        require(_to != address(0));
        require(_value <= balances[_from]);
        require(_value <= allowed[_from][msg.sender]); 
        balances[_from] = balances[_from] - _value; 
        balances[_to] = balances[_to] + _value; 
        allowed[_from][msg.sender] = allowed[_from][msg.sender] - _value; 
        emit Transfer(_from, _to, _value); 
        return true; 
        } 
 
     function increaseApproval(address _spender, uint _addedValue) public returns (bool) { 
     allowed[msg.sender][_spender] = allowed[msg.sender][_spender] + _addedValue; 
     emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); 
     return true; 
     } 
 
    function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) { 
    uint oldValue = allowed[msg.sender][_spender]; 
    if (_subtractedValue > oldValue) {
 
        allowed[msg.sender][_spender] = 0;
    } 
        else {
        allowed[msg.sender][_spender] = oldValue - _subtractedValue;
    }
    emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
    }
	

 
    constructor() public {
        totalSupply_ = initialSupply;
        balances[this] = initialSupply;
		buyPrice = 0.001 ether;
		owner = msg.sender;
    }
}

Итак, добавлены переменные buyPrice и owner, которые хранят цену токена и адрес создателя соответственно.

Добавлена внутренняя функция _transfer. Так как оперировать приходится с адресом контракта, то для нормальной работы требуется указывать три параметра (address _from, address _to, uint256 _value), а функция transfer, согласно стандаарту ERC20 должна брать только 2. Поэтому из функции transfer будем вызывать внутреннюю функцию _transfer.

Теперь посмотрим на функции приема эфиров, их две. Одна безымянная:

function() public payable{
   _buy(msg.sender, msg.value);
}

Вторая под именем buy:

function buy() public payable {
	_buy(msg.sender, msg.value);
}

Модификатор payable говорит о том, что эти функции могут принимать входящий эфир и что-то с ним делать. В нашем случае они запускают внутреннюю internal функцию _buy.

Зачем две функции, принимающие эфир? Первая безымянная срабатывает если пользователь просто пришлет эфир со своего адреса, вторая с именем buy нужна для того, чтобы можно было сделать javascript-функцию из личного кабинета инвестора, например. Если это не планируется, то ее можно вообще удалить.

Итак, обе функции принимают эфир и после запускают внутреннюю функцию _buy:

function _buy(address _from, uint256 _value) internal {
	uint256 _amount = (_value / buyPrice)*(10**uint256(decimals));
	_transfer(this, _from, _amount);
	emit Transfer(this, _from, _amount);
}

Функция берет два параметра address _from, uint256 _value: адрес пославшего эфир и количество этого эфира. Надеюсь, вы помните, что Solidity оперирует гвеями, а не эфирами. Наш токен стоит 0,001 ETH (указали это в конструкторе), в гвеях это будет 1000000000000000.

Далее происходит вычисление количества токенов, которые нужно отправить пользователю:

uint256 _amount = (_value / buyPrice)*(10**uint256(decimals));

Делим присланное количество эфиров на цену токена, а затем умножаем на 10 в степени decimals, то есть в нашем случае 1018.  Зачем умножать на decimals? Дело в том, что наш токен имеет 18 нулей после запятой, то есть минимальная копейка будет выглядеть так:

0,000000000000000001

Если не умножить на 10**uint256(decimals), то именно эта минимальная копейка и будет стоить 0,001 ETH, а нужно чтобы целый токен стоил столько.

В конструкторе ничего особо сложного нет:

constructor() public {
        totalSupply_ = initialSupply;
        balances[this] = initialSupply;
	buyPrice = 0.001 ether;
	owner = msg.sender;
    }

С помощью ключевого слова this присваиваем все созданные токены адресу контракта, цена токена buyPrice, чтобы не плодить кучу нулей, указана с ключевым словом ether. Solidity сам преобразует такую запись в гвеи. В переменную owner записывается адрес создателя контракта, чтобы с помощью функции transferEthers можно было вывести эфиры на свой адрес. Для этого владелец должен зайти в MyEtherWallet и запустить эту функцию, подробнее об этом писал в статье Как взаимодействовать со смарт-контрактом.

Все остальные функции рассматривал в других статьях, поэтому на них останавливаться не буду.

Рабочий контракт можно посмотреть здесь. Любой, отправивший 0,001 Эфира, получит 1 токен CCT.

В следующих статьях я опишу более профессиональный подход к созданию ICO-контрактов.

Как задеплоить контракт в блокчейн Эфира, смотрите в статье Как создать ETH токен

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Так как вы нашли эту публикацию полезной...

Подписывайтесь на нас в соцсетях!

Создаем смарт-контракт для ICO. Часть 1: 8 комментариев

  • 29.08.2018 в 21:16
    Permalink

    Почему нельзя для ICO использовать?

    Ответ
    • 13.09.2018 в 19:45
      Permalink

      Потому что этот код я смастерил на быструю руку, лижбы поскорей. В профессиональном мире все контракты пишут на основе библиотеки OpenZeppelin, так как в ней учтены все возможные ошибки.

      Ответ
  • 15.01.2019 в 17:51
    Permalink

    я не могу понять, как снять эфиры после продажи токенов. как это сделать?

    Ответ
    • 16.01.2019 в 10:18
      Permalink

      Надо запустить функцию transferEthers() через MyEtherWallet. Она отправит все накопленные эфиры на адрес создателя контракта. Как запустить функцию читай в статье «Как взаимодействовать с контрактом» https://happyhodler.ru/ethereum/interact-with-contract/

      Ответ
  • 14.09.2019 в 03:16
    Permalink

    admin, нужна ваша помощь по смарт контрактам, как с вами связаться?

    Ответ
    • 16.09.2019 в 17:15
      Permalink

      Я больше не занимаюсь смарт-контрактами

      Ответ
  • 10.09.2020 в 09:38
    Permalink

    Хотел уточнить насчет одного момента, если перейти на ваш смарт контракт, то видно что когда пользователь отправляет вам эфиры ему в ответ возвращаются токены, все работает как надо, но почему в etherscan дублируется хэш такой транзакции, и кажется как будто дважды отправлено одно и тоже количество токенов, хотя эти транзакции отображаемые на etherscan просто с одним и тем же хэшем

    Ответ

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *