Разбор смарт-контракт проекта MyWish

Разбор смарт-контракт проекта MyWish

den_rodionov

pragma solidity ^0.4.18;


/**

 * Контракт Ownable предоставляет базовые функции и модификаторы для контроля авторизации пользователя и управления пользовательскими правами.

 */

 

contract Ownable {

   address public owner;

   event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

 

 

   /**

    * Присваивает роль владельца смартконтракта адресу отправителя транзакции деплоймента смарт контракта.

    */

 

   function Ownable() {

       owner = msg.sender;

   }



   /**

    * Проверяет наличие роли владельца смарт контракта.

    */

 

   modifier onlyOwner() {

       require(msg.sender == owner);

       _;

   }



   /**

    * Позволяет адресу с ролью владельца смарт контракта передавать эту роль другому адресу

    */

 

   function transferOwnership(address newOwner) onlyOwner {

       require(newOwner != address(0));

       OwnershipTransferred(owner, newOwner);

       owner = newOwner;

   }


}


contract usingMyWishConsts {

   uint constant TOKEN_DECIMALS = 18;

   uint8 constant TOKEN_DECIMALS_UINT8 = 18;

   uint constant TOKEN_DECIMAL_MULTIPLIER = 10 ** TOKEN_DECIMALS;


   uint constant TEAM_TOKENS =  3161200 * TOKEN_DECIMAL_MULTIPLIER;

   uint constant BOUNTY_TOKENS = 2000000 * TOKEN_DECIMAL_MULTIPLIER;

   uint constant PREICO_TOKENS = 3038800 * TOKEN_DECIMAL_MULTIPLIER;

   uint constant MINIMAL_PURCHASE = 0.05 ether;


   address constant TEAM_ADDRESS = 0xE4F0Ff4641f3c99de342b06c06414d94A585eFfb;

   address constant BOUNTY_ADDRESS = 0x76d4136d6EE53DB4cc087F2E2990283d5317A5e9;

   address constant PREICO_ADDRESS = 0x195610851A43E9685643A8F3b49F0F8a019204f1;

   address constant COLD_WALLET = 0x80826b5b717aDd3E840343364EC9d971FBa3955C;


   string constant TOKEN_NAME = "MyWish Token";

   bytes32 constant TOKEN_SYMBOL = "WISH";

}


/**

 * Библиотека безопасной математики с выбросом исключений при ошибках

 */

 

library SafeMath {

 function mul(uint256 a, uint256 b) internal constant returns (uint256) {

   uint256 c = a * b;

   assert(a == 0 || c / a == b);

   return c;

 }


 function div(uint256 a, uint256 b) internal constant returns (uint256) {

   uint256 c = a / b;

   return c;

 }


 function sub(uint256 a, uint256 b) internal constant returns (uint256) {

   assert(b <= a);

   return a - b;

 }


 function add(uint256 a, uint256 b) internal constant returns (uint256) {

   uint256 c = a + b;

   assert(c >= a);

   return c;

 }

}


/**

 * Объявление базовых функций стандартного ERC20 токена

 */

 

contract ERC20Basic {

 uint256 public totalSupply;

 function balanceOf(address who) constant returns (uint256);

 function transfer(address to, uint256 value) returns (bool);

 event Transfer(address indexed from, address indexed to, uint256 value);

}


/**

 * Объявление расширенных функций стандартного ERC20 токена

 */

 

contract ERC20 is ERC20Basic {

 function allowance(address owner, address spender) constant returns (uint256);

 function transferFrom(address from, address to, uint256 value) returns (bool);

 function approve(address spender, uint256 value) returns (bool);

 event Approval(address indexed owner, address indexed spender, uint256 value);

}


/**

 * Реализация базовых функций стандартного ERC20 токена

 */

 

contract BasicToken is ERC20Basic {

   using SafeMath for uint256;


   mapping (address => uint256) balances;

 

   /**

   * Функция перевода токенов

   */

 

   function transfer(address _to, uint256 _value) returns (bool) {

       require(_to != address(0));


       // SafeMath.sub will throw if there is not enough balance.

       balances[msg.sender] = balances[msg.sender].sub(_value);

       balances[_to] = balances[_to].add(_value);

       Transfer(msg.sender, _to, _value);

       return true;

   }


   /**

   * функция запроса баланса токенов на определённом адресе

   */

 

   function balanceOf(address _owner) constant returns (uint256 balance) {

       return balances[_owner];

   }


}


/**

 * Реализация расширенных функций стандартного ERC20 токена

 */

 

contract StandardToken is ERC20, BasicToken {


   mapping (address => mapping (address => uint256)) allowed;

 

 

   /**

    * Функция перевода токенов с другого адреса (возможно при делегировании такого права владельцем токенов)

    */

 

   function transferFrom(address _from, address _to, uint256 _value) returns (bool) {

       require(_to != address(0));


       var _allowance = allowed[_from][msg.sender];

 

       // В проверке прав нет необходимости, так как функция sub вызовет исключение в случае, если _value > _allowance


       balances[_from] = balances[_from].sub(_value);

       balances[_to] = balances[_to].add(_value);

       allowed[_from][msg.sender] = _allowance.sub(_value);

       Transfer(_from, _to, _value);

       return true;

   }


   /**

    * Функция делегирования прав расходования токенов

    */


   function approve(address _spender, uint256 _value) returns (bool) {

 

       // для выполнения функции при ненулевом делегировании сначала необходимо обнулить количество делегированных токенов

       

require((_value == 0) || (allowed[msg.sender][_spender] == 0));


       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];

   }



   /**

    * Функции для увеличения и уменьшения количества делегированных токенов при ненулевом делегировании

    */

 

   function increaseApproval(address _spender, uint _addedValue) returns (bool success) {

       allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);

       Approval(msg.sender, _spender, allowed[msg.sender][_spender]);

       return true;

   }


   function decreaseApproval(address _spender, uint _subtractedValue) returns (bool success) {

       uint oldValue = allowed[msg.sender][_spender];

       if (_subtractedValue > oldValue) {

           allowed[msg.sender][_spender] = 0;

       }

       else {

           allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);

       }

       Approval(msg.sender, _spender, allowed[msg.sender][_spender]);

       return true;

   }


}


/**

 * Контракт, реализующий выпуск токенов на определённый адрес с проверкой прав владельца контракта

 */

 

contract MintableToken is StandardToken, Ownable {

   event Mint(address indexed to, uint256 amount);


   event MintFinished();


   bool public mintingFinished = false;



   modifier canMint() {

       require(!mintingFinished);

       _;

   }


   /**

    * Функция выпуска токена с проверкой возможности выпуска (mintingFinished == false)

    */

  

 function mint(address _to, uint256 _amount) onlyOwner canMint returns (bool) {

       totalSupply = totalSupply.add(_amount);

       balances[_to] = balances[_to].add(_amount);

       Mint(_to, _amount);

       Transfer(0x0, _to, _amount);

       return true;

   }


   /**

    * Функция закрытия выпуска токенов

    */

 

   function finishMinting() onlyOwner returns (bool) {

       mintingFinished = true;

       MintFinished();

       return true;

   }

}


/**

 * Контракт с функцией сжигания токенов

 */


contract BurnableToken is StandardToken {


   event Burn(address indexed burner, uint256 value);


   function burn(uint256 _value) public {

       require(_value > 0);


       address burner = msg.sender;

       balances[burner] = balances[burner].sub(_value);

       totalSupply = totalSupply.sub(_value);

       Burn(burner, _value);

   }

}

 

// Собственно контракт токена MyWish

 

contract MyWishToken is usingMyWishConsts, MintableToken, BurnableToken {

  

 /**

    * Остановка переводов токенов между аккаунтами на период crowdsale

    */

 

  bool public paused = true;

 

 /**

    * Аккаунты, которым разрешены переводы в период остановки переводов.

    */

 

   mapping(address => bool) excluded;


   function name() constant public returns (string _name) {

       return TOKEN_NAME;

   }


   function symbol() constant public returns (bytes32 _symbol) {

       return TOKEN_SYMBOL;

   }


   function decimals() constant public returns (uint8 _decimals) {

       return TOKEN_DECIMALS_UINT8;

   }


   function crowdsaleFinished() onlyOwner {

       paused = false;

       finishMinting();

   }


   function addExcluded(address _toExclude) onlyOwner {

       excluded[_toExclude] = true;

   }


   function transferFrom(address _from, address _to, uint256 _value) returns (bool) {

       require(!paused || excluded[_from]);

       return super.transferFrom(_from, _to, _value);

   }


   function transfer(address _to, uint256 _value) returns (bool) {

       require(!paused || excluded[msg.sender]);

       return super.transfer(_to, _value);

   }

 

   /**

    * Функция сжигания делегированных токенов

    */

   

function burnFrom(address _from, uint256 _value) returns (bool) {

       require(_value > 0);

       var allowance = allowed[_from][msg.sender];

       balances[_from] = balances[_from].sub(_value);

       totalSupply = totalSupply.sub(_value);

       allowed[_from][msg.sender] = allowance.sub(_value);

       Burn(_from, _value);

       return true;

   }

}

contract MyWishRateProviderI {


   /**

    * Объявление функций для определения стоимости токена

    */


   function getRate(address buyer, uint totalSold, uint amountWei) public constant returns (uint);

 

   /**

    * Поддержка не целых значений стоимости

    */

 

 function getRateScale() public constant returns (uint);

 

   /**

    * Базовая ставка стоимости

    */

 

   function getBaseRate() public constant returns (uint);

}


contract MyWishRateProvider is usingMyWishConsts, MyWishRateProviderI, Ownable {

   // точность расчетов

   uint constant RATE_SCALE = 10000;

   uint constant STEP_30 = 3200000 * TOKEN_DECIMAL_MULTIPLIER;

   uint constant STEP_20 = 6400000 * TOKEN_DECIMAL_MULTIPLIER;

   uint constant STEP_10 = 9600000 * TOKEN_DECIMAL_MULTIPLIER;

   uint constant RATE_30 = 1950 * RATE_SCALE;

   uint constant RATE_20 = 1800 * RATE_SCALE;

   uint constant RATE_10 = 1650 * RATE_SCALE;

   uint constant BASE_RATE = 1500 * RATE_SCALE;


   struct ExclusiveRate {

       uint32 workUntil;

       uint rate;

       uint16 bonusPercent1000;

       bool exists;

   }


   mapping(address => ExclusiveRate) exclusiveRate;


   function getRateScale() public constant returns (uint) {

       return RATE_SCALE;

   }


   function getBaseRate() public constant returns (uint) {

       return BASE_RATE;

   }


   function getRate(address buyer, uint totalSold, uint amountWei) public constant returns (uint) {

       uint rate;


       // реализация периодов распродажи


       if (totalSold < STEP_30) {

           rate = RATE_30;

       }

       else if (totalSold < STEP_20) {

           rate = RATE_20;

       }

       else if (totalSold < STEP_10) {

           rate = RATE_10;

       }

       else {

           rate = BASE_RATE;

       }


       // реализация бонусов за объем


       if (amountWei >= 1000 ether) {

           rate += rate * 13 / 100;

       }

       else if (amountWei >= 500 ether) {

           rate += rate * 10 / 100;

       }

       else if (amountWei >= 100 ether) {

           rate += rate * 7 / 100;

       }

       else if (amountWei >= 50 ether) {

           rate += rate * 5 / 100;

       }

       else if (amountWei >= 30 ether) {

           rate += rate * 4 / 100;

       }

       else if (amountWei >= 10 ether) {

           rate += rate * 25 / 1000;

       }


       ExclusiveRate memory eRate = exclusiveRate[buyer];

       if (eRate.exists && eRate.workUntil >= now) {

           if (eRate.rate != 0) {

               rate = eRate.rate;

           }

           rate += rate * eRate.bonusPercent1000 / 1000;

       }

       return rate;

   }


   function setExclusiveRate(address _investor, uint _rate, uint16 _bonusPercent1000, uint32 _workUntil) onlyOwner {

       exclusiveRate[_investor] = ExclusiveRate(_workUntil, _rate, _bonusPercent1000, true);

   }


   function removeExclusiveRate(address _investor) onlyOwner {

       delete exclusiveRate[_investor];

   }

}


/**

 * Базовый контракт crowdsale.

 * У контракта есть даты начала и конца, токены, рассчитанные по объявленной стоимости перечисляются на адрес отправителя ETH.

 */

 

contract Crowdsale {

   using SafeMath for uint;

 

   // Токены для продажи

 

   MintableToken public token;

 

   // Начало и конец crowdsale


   uint32 internal startTime;

   uint32 internal endTime;


   // адрес, где будут собираться средства


   address public wallet;


   // Количество собранных средств в wei


   uint public weiRaised;


   /**

    * Количество проданных токенов

    */


   uint public soldTokens;


   /**

    * Максимальное количество выпускаемых токеов

    */


   uint internal hardCap;


   /**

    * Событие для логирования покупки токенов

    */

 

   event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint value, uint amount);


   function Crowdsale(uint _startTime, uint _endTime, uint _hardCap, address _wallet) {

       require(_endTime >= _startTime);

       require(_wallet != 0x0);

       require(_hardCap > 0);


       token = createTokenContract();

       startTime = uint32(_startTime);

       endTime = uint32(_endTime);

       hardCap = _hardCap;

       wallet = _wallet;

   }


   function createTokenContract() internal returns (MintableToken) {

       return new MintableToken();

   }


   function getRate(uint amount) internal constant returns (uint);


   function getBaseRate() internal constant returns (uint);


   function getRateScale() internal constant returns (uint) {

       return 1;

   }


   // Функция по определению (fallback) будет запущена, если не указаны функция в вызове контракта (при непосредственном переводе средств на адрес смарт контракта)


   function() payable {

       buyTokens(msg.sender, msg.value);

   }

 

   // Функция, реализующая процесс покупки

 

   function buyTokens(address beneficiary, uint amountWei) internal {

       require(beneficiary != 0x0);


       // общее количество выпущенных токенов


       uint totalSupply = token.totalSupply();


       // текущая ставка стоимости токена с учетом бонусов и скидок


       uint actualRate = getRate(amountWei);

       uint rateScale = getRateScale();


       require(validPurchase(amountWei, actualRate, totalSupply));

 

       // вычисление количества токенов для выпуска


       uint tokens = amountWei.mul(actualRate).div(rateScale);

 

       // обновление состояния смартконтракта

 

       weiRaised = weiRaised.add(amountWei);

       soldTokens = soldTokens.add(tokens);


       token.mint(beneficiary, tokens);

       TokenPurchase(msg.sender, beneficiary, amountWei, tokens);


       forwardFunds(amountWei);

   }


   // отправляет полученные смарт контрактом средства на адрес для сбора средств

 

  function forwardFunds(uint amountWei) internal {

       wallet.transfer(amountWei);

   }


   /**

    * Проверка валидности операции покупки токенов

    */

   

function validPurchase(uint _amountWei, uint _actualRate, uint _totalSupply) internal constant returns (bool) {

       bool withinPeriod = now >= startTime && now <= endTime;

       bool nonZeroPurchase = _amountWei != 0;

       bool hardCapNotReached = _totalSupply <= hardCap;


       return withinPeriod && nonZeroPurchase && hardCapNotReached;

   }


   function hasEnded() public constant returns (bool) {

       return now > endTime || token.totalSupply() > hardCap;

   }


   function hasStarted() public constant returns (bool) {

       return now >= startTime;

   }

}


// Контракт, реализующий возможность остановки crowdsale

 

contract FinalizableCrowdsale is Crowdsale, Ownable {

   using SafeMath for uint256;


   bool public isFinalized = false;


   event Finalized();


   function FinalizableCrowdsale(uint _startTime, uint _endTime, uint _hardCap, address _wallet)

           Crowdsale(_startTime, _endTime, _hardCap, _wallet) {

   }


   /**

    * Функция для остановки crowdsale

    */

   

function finalize() onlyOwner notFinalized {

       require(hasEnded());


       finalization();

       Finalized();


       isFinalized = true;

   }


   function finalization() internal {

   }


   modifier notFinalized() {

       require(!isFinalized);

       _;

   }

}


// Контракт, объединяющий функции базовых контрактов для реализации crowdsale с возможностью переопределения базовых функций


contract MyWishCrowdsale is usingMyWishConsts, FinalizableCrowdsale {

   MyWishRateProviderI public rateProvider;


   function MyWishCrowdsale(

           uint _startTime,

           uint _endTime,

           uint _hardCapTokens

   )

           FinalizableCrowdsale(_startTime, _endTime, _hardCapTokens * TOKEN_DECIMAL_MULTIPLIER, COLD_WALLET) {


       token.mint(TEAM_ADDRESS, TEAM_TOKENS);

       token.mint(BOUNTY_ADDRESS, BOUNTY_TOKENS);

       token.mint(PREICO_ADDRESS, PREICO_TOKENS);


       MyWishToken(token).addExcluded(TEAM_ADDRESS);

       MyWishToken(token).addExcluded(BOUNTY_ADDRESS);

       MyWishToken(token).addExcluded(PREICO_ADDRESS);


       MyWishRateProvider provider = new MyWishRateProvider();

       provider.transferOwnership(owner);

       rateProvider = provider;

   }


   /**

    * Переопределяет процесс создания токена для интеграции со смарт-контрактом MyWish

    */


   function createTokenContract() internal returns (MintableToken) {

       return new MyWishToken();

   }


   /**

    * Переопределение функций получения стоимости токена в соответствии с указанным источником ставок

    */

 

   function getRate(uint _value) internal constant returns (uint) {

       return rateProvider.getRate(msg.sender, soldTokens, _value);

   }


   function getBaseRate() internal constant returns (uint) {

       return rateProvider.getRate(msg.sender, soldTokens, MINIMAL_PURCHASE);

   }



   function getRateScale() internal constant returns (uint) {

       return rateProvider.getRateScale();

   }


   /**

    * Владелец контракта может менять источник ставок

    */

 

   function setRateProvider(address _rateProviderAddress) onlyOwner {

       require(_rateProviderAddress != 0);

       rateProvider = MyWishRateProviderI(_rateProviderAddress);

   }


   /**

    * Функции, предоставляющие администратору менять базовые установки смарт контракта до его финализации

    */

 

   function setEndTime(uint _endTime) onlyOwner notFinalized {

       require(_endTime > startTime);

       endTime = uint32(_endTime);

   }

 

   function setHardCap(uint _hardCapTokens) onlyOwner notFinalized {

       require(_hardCapTokens * TOKEN_DECIMAL_MULTIPLIER > hardCap);

       hardCap = _hardCapTokens * TOKEN_DECIMAL_MULTIPLIER;

   }


   function setStartTime(uint _startTime) onlyOwner notFinalized {

       require(_startTime < endTime);

       startTime = uint32(_startTime);

   }


   function addExcluded(address _address) onlyOwner notFinalized {

       MyWishToken(token).addExcluded(_address);

   }


   function validPurchase(uint _amountWei, uint _actualRate, uint _totalSupply) internal constant returns (bool) {

       if (_amountWei < MINIMAL_PURCHASE) {

           return false;

       }

       return super.validPurchase(_amountWei, _actualRate, _totalSupply);

   }


   // Финализация, после которой изменение настроек смарт контракта будет невозможным


   function finalization() internal {

       super.finalization();

       token.finishMinting();

       MyWishToken(token).crowdsaleFinished();

       token.transferOwnership(owner);

   }

}



Перевод комментариев к контракту подготовила команда сайта ICO Digest – каталога наиболее перспективных и надёжных блокчейн-проектов, решивших провести собственный токенсейл.


Материал подготовил Den_Rodionov

Report Page