ETH Price: $1,897.12 (-1.01%)

Transaction Decoder

Block:
5589787 at May-10-2018 02:39:33 PM +UTC
Transaction Fee:
0.0053202 ETH $10.09
Gas Used:
88,670 Gas / 60 Gwei

Emitted Events:

5 UBCoinToken.Mint( to=[Receiver] ICO, amount=1000000000000000000000 )
6 UBCoinToken.Transfer( from=[Receiver] ICO, to=[Sender] 0xa445729d99fbf73afc450c10adfb427794c9f314, value=1000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
(DwarfPool)
382.433736275042309456 Eth382.439056475042309456 Eth0.0053202
0x2D3E7D48...83E4c38d5
0x5FB78D8B...c5783356F 2,213.914514111067088058 Eth2,214.014514111067088058 Eth0.1
0x8DD9034f...2EB94A67a
(Ubcoin: Token Sale)
0xA445729d...794C9F314
0.120236211562414514 Eth
Nonce: 34
0.014916011562414514 Eth
Nonce: 35
0.1053202

Execution Trace

ETH 0.1 ICO.CALL( )
  • ETH 0.1 0x5fb78d8b8f1161731bc80ef93cbcfccc5783356f.CALL( )
  • UBCoinToken.mint( _to=0x8DD9034f7cCC805bDc4D593A01f6A2E2EB94A67a, _amount=1000000000000000000000 ) => ( True )
  • UBCoinToken.transfer( _to=0xA445729d99FBF73AfC450C10AdFB427794C9F314, _value=1000000000000000000000 ) => ( True )
    File 1 of 2: ICO
    pragma solidity ^0.4.18;
    
    // File: contracts/ownership/Ownable.sol
    
    /**
     * @title Ownable
     * @dev The Ownable contract has an owner address, and provides basic authorization control
     * functions, this simplifies the implementation of "user permissions".
     */
    contract Ownable {
      address public owner;
    
    
      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    
      /**
       * @dev The Ownable constructor sets the original `owner` of the contract to the sender
       * account.
       */
      function Ownable() public {
        owner = msg.sender;
      }
    
    
      /**
       * @dev Throws if called by any account other than the owner.
       */
      modifier onlyOwner() {
        require(msg.sender == owner);
        _;
      }
    
    
      /**
       * @dev Allows the current owner to transfer control of the contract to a newOwner.
       * @param newOwner The address to transfer ownership to.
       */
      function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0));
        OwnershipTransferred(owner, newOwner);
        owner = newOwner;
      }
    
    }
    
    // File: contracts/math/SafeMath.sol
    
    /**
     * @title SafeMath
     * @dev Math operations with safety checks that throw on error
     */
    library SafeMath {
      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
          return 0;
        }
        uint256 c = a * b;
        assert(c / a == b);
        return c;
      }
    
      function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // assert(b > 0); // Solidity automatically throws when dividing by 0
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return c;
      }
    
      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
      }
    
      function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
      }
    }
    
    // File: contracts/token/ERC20Basic.sol
    
    /**
     * @title ERC20Basic
     * @dev Simpler version of ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/179
     */
    contract ERC20Basic {
      uint256 public totalSupply;
      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);
    }
    
    // File: contracts/token/BasicToken.sol
    
    /**
     * @title Basic token
     * @dev Basic version of StandardToken, with no allowances.
     */
    contract BasicToken is ERC20Basic {
      using SafeMath for uint256;
    
      mapping(address => uint256) balances;
    
      /**
      * @dev transfer token for a specified address
      * @param _to The address to transfer to.
      * @param _value The amount to be transferred.
      */
      function transfer(address _to, uint256 _value) public returns (bool) {
        require(_to != address(0));
        require(_value <= balances[msg.sender]);
    
        // 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;
      }
    
      /**
      * @dev Gets the balance of the specified address.
      * @param _owner The address to query the the balance of.
      * @return An uint256 representing the amount owned by the passed address.
      */
      function balanceOf(address _owner) public view returns (uint256 balance) {
        return balances[_owner];
      }
    
    }
    
    // File: contracts/token/ERC20.sol
    
    /**
     * @title ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    contract ERC20 is ERC20Basic {
      function allowance(address owner, address spender) public view returns (uint256);
      function transferFrom(address from, address to, uint256 value) public returns (bool);
      function approve(address spender, uint256 value) public returns (bool);
      event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    
    // File: contracts/token/StandardToken.sol
    
    /**
     * @title Standard ERC20 token
     *
     * @dev Implementation of the basic standard token.
     * @dev https://github.com/ethereum/EIPs/issues/20
     * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
     */
    contract StandardToken is ERC20, BasicToken {
    
      mapping (address => mapping (address => uint256)) internal allowed;
    
    
      /**
       * @dev Transfer tokens from one address to another
       * @param _from address The address which you want to send tokens from
       * @param _to address The address which you want to transfer to
       * @param _value uint256 the amount of tokens to be transferred
       */
      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].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        Transfer(_from, _to, _value);
        return true;
      }
    
      /**
       * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
       *
       * Beware that changing an allowance with this method brings the risk that someone may use both the old
       * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
       * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
       * @param _spender The address which will spend the funds.
       * @param _value The amount of tokens to be spent.
       */
      function approve(address _spender, uint256 _value) public returns (bool) {
        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
      }
    
      /**
       * @dev Function to check the amount of tokens that an owner allowed to a spender.
       * @param _owner address The address which owns the funds.
       * @param _spender address The address which will spend the funds.
       * @return A uint256 specifying the amount of tokens still available for the spender.
       */
      function allowance(address _owner, address _spender) public view returns (uint256) {
        return allowed[_owner][_spender];
      }
    
      /**
       * @dev Increase the amount of tokens that an owner allowed to a spender.
       *
       * approve should be called when allowed[_spender] == 0. To increment
       * allowed value is better to use this function to avoid 2 calls (and wait until
       * the first transaction is mined)
       * From MonolithDAO Token.sol
       * @param _spender The address which will spend the funds.
       * @param _addedValue The amount of tokens to increase the allowance by.
       */
      function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
        allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
        Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
      }
    
      /**
       * @dev Decrease the amount of tokens that an owner allowed to a spender.
       *
       * approve should be called when allowed[_spender] == 0. To decrement
       * allowed value is better to use this function to avoid 2 calls (and wait until
       * the first transaction is mined)
       * From MonolithDAO Token.sol
       * @param _spender The address which will spend the funds.
       * @param _subtractedValue The amount of tokens to decrease the allowance by.
       */
      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.sub(_subtractedValue);
        }
        Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
      }
    
    }
    
    // File: contracts/MintableToken.sol
    
    contract MintableToken is StandardToken, Ownable {
    
      event Mint(address indexed to, uint256 amount);
    
      event MintFinished();
    
      bool public mintingFinished = false;
    
      address public saleAgent;
    
      modifier notLocked() {
        require(msg.sender == owner || msg.sender == saleAgent || mintingFinished);
        _;
      }
    
      function setSaleAgent(address newSaleAgnet) public {
        require(msg.sender == saleAgent || msg.sender == owner);
        saleAgent = newSaleAgnet;
      }
    
      function mint(address _to, uint256 _amount) public returns (bool) {
        require((msg.sender == saleAgent || msg.sender == owner) && !mintingFinished);
        
        totalSupply = totalSupply.add(_amount);
        balances[_to] = balances[_to].add(_amount);
        Mint(_to, _amount);
        return true;
      }
    
      /**
       * @dev Function to stop minting new tokens.
       * @return True if the operation was successful.
       */
      function finishMinting() public returns (bool) {
        require((msg.sender == saleAgent || msg.sender == owner) && !mintingFinished);
        mintingFinished = true;
        MintFinished();
        return true;
      }
    
      function transfer(address _to, uint256 _value) public notLocked returns (bool) {
        return super.transfer(_to, _value);
      }
    
      function transferFrom(address from, address to, uint256 value) public notLocked returns (bool) {
        return super.transferFrom(from, to, value);
      }
    
    }
    
    // File: contracts/FreezeTokensWallet.sol
    
    contract FreezeTokensWallet is Ownable {
    
      using SafeMath for uint256;
    
      MintableToken public token;
    
      bool public started;
    
      uint public startLockPeriod = 180 days;
    
      uint public period = 360 days;
    
      uint public duration = 90 days;
    
      uint public startUnlock;
    
      uint public retrievedTokens;
    
      uint public startBalance;
    
      modifier notStarted() {
        require(!started);
        _;
      }
    
      function setPeriod(uint newPeriod) public onlyOwner notStarted {
        period = newPeriod * 1 days;
      }
    
      function setDuration(uint newDuration) public onlyOwner notStarted {
        duration = newDuration * 1 days;
      }
    
      function setStartLockPeriod(uint newStartLockPeriod) public onlyOwner notStarted {
        startLockPeriod = newStartLockPeriod * 1 days;
      }
    
      function setToken(address newToken) public onlyOwner notStarted {
        token = MintableToken(newToken);
      }
    
      function start() public onlyOwner notStarted {
        startUnlock = now + startLockPeriod;
        retrievedTokens = 0;
        startBalance = token.balanceOf(this);
        started = true;
      }
    
      function retrieveTokens(address to) public onlyOwner {
        require(started && now >= startUnlock);
        if (now >= startUnlock + period) {
          token.transfer(to, token.balanceOf(this));
        } else {
          uint parts = period.div(duration);
          uint tokensByPart = startBalance.div(parts);
          uint timeSinceStart = now.sub(startUnlock);
          uint pastParts = timeSinceStart.div(duration);
          uint tokensToRetrieveSinceStart = pastParts.mul(tokensByPart);
          uint tokensToRetrieve = tokensToRetrieveSinceStart.sub(retrievedTokens);
          if(tokensToRetrieve > 0) {
            retrievedTokens = retrievedTokens.add(tokensToRetrieve);
            token.transfer(to, tokensToRetrieve);
          }
        }
      }
    }
    
    // File: contracts/InvestedProvider.sol
    
    contract InvestedProvider is Ownable {
    
      uint public invested;
    
    }
    
    // File: contracts/PercentRateProvider.sol
    
    contract PercentRateProvider is Ownable {
    
      uint public percentRate = 100;
    
      function setPercentRate(uint newPercentRate) public onlyOwner {
        percentRate = newPercentRate;
      }
    
    }
    
    // File: contracts/RetrieveTokensFeature.sol
    
    contract RetrieveTokensFeature is Ownable {
    
      function retrieveTokens(address to, address anotherToken) public onlyOwner {
        ERC20 alienToken = ERC20(anotherToken);
        alienToken.transfer(to, alienToken.balanceOf(this));
      }
    
    }
    
    // File: contracts/WalletProvider.sol
    
    contract WalletProvider is Ownable {
    
      address public wallet;
    
      function setWallet(address newWallet) public onlyOwner {
        wallet = newWallet;
      }
    
    }
    
    // File: contracts/CommonSale.sol
    
    contract CommonSale is InvestedProvider, WalletProvider, PercentRateProvider, RetrieveTokensFeature {
    
      using SafeMath for uint;
    
      address public directMintAgent;
    
      uint public price;
    
      uint public start;
    
      uint public minInvestedLimit;
    
      MintableToken public token;
    
      uint public hardcap;
    
      modifier isUnderHardcap() {
        require(invested < hardcap);
        _;
      }
    
      function setHardcap(uint newHardcap) public onlyOwner {
        hardcap = newHardcap;
      }
    
      modifier onlyDirectMintAgentOrOwner() {
        require(directMintAgent == msg.sender || owner == msg.sender);
        _;
      }
    
      modifier minInvestLimited(uint value) {
        require(value >= minInvestedLimit);
        _;
      }
    
      function setStart(uint newStart) public onlyOwner {
        start = newStart;
      }
    
      function setMinInvestedLimit(uint newMinInvestedLimit) public onlyOwner {
        minInvestedLimit = newMinInvestedLimit;
      }
    
      function setDirectMintAgent(address newDirectMintAgent) public onlyOwner {
        directMintAgent = newDirectMintAgent;
      }
    
      function setPrice(uint newPrice) public onlyOwner {
        price = newPrice;
      }
    
      function setToken(address newToken) public onlyOwner {
        token = MintableToken(newToken);
      }
    
      function calculateTokens(uint _invested) internal returns(uint);
    
      function mintTokensExternal(address to, uint tokens) public onlyDirectMintAgentOrOwner {
        mintTokens(to, tokens);
      }
    
      function mintTokens(address to, uint tokens) internal {
        token.mint(this, tokens);
        token.transfer(to, tokens);
      }
    
      function endSaleDate() public view returns(uint);
    
      function mintTokensByETHExternal(address to, uint _invested) public onlyDirectMintAgentOrOwner returns(uint) {
        return mintTokensByETH(to, _invested);
      }
    
      function mintTokensByETH(address to, uint _invested) internal isUnderHardcap returns(uint) {
        invested = invested.add(_invested);
        uint tokens = calculateTokens(_invested);
        mintTokens(to, tokens);
        return tokens;
      }
    
      function fallback() internal minInvestLimited(msg.value) returns(uint) {
        require(now >= start && now < endSaleDate());
        wallet.transfer(msg.value);
        return mintTokensByETH(msg.sender, msg.value);
      }
    
      function () public payable {
        fallback();
      }
    
    }
    
    // File: contracts/StagedCrowdsale.sol
    
    contract StagedCrowdsale is Ownable {
    
      using SafeMath for uint;
    
      struct Milestone {
        uint period;
        uint bonus;
      }
    
      uint public totalPeriod;
    
      Milestone[] public milestones;
    
      function milestonesCount() public view returns(uint) {
        return milestones.length;
      }
    
      function addMilestone(uint period, uint bonus) public onlyOwner {
        require(period > 0);
        milestones.push(Milestone(period, bonus));
        totalPeriod = totalPeriod.add(period);
      }
    
      function removeMilestone(uint8 number) public onlyOwner {
        require(number < milestones.length);
        Milestone storage milestone = milestones[number];
        totalPeriod = totalPeriod.sub(milestone.period);
    
        delete milestones[number];
    
        for (uint i = number; i < milestones.length - 1; i++) {
          milestones[i] = milestones[i+1];
        }
    
        milestones.length--;
      }
    
      function changeMilestone(uint8 number, uint period, uint bonus) public onlyOwner {
        require(number < milestones.length);
        Milestone storage milestone = milestones[number];
    
        totalPeriod = totalPeriod.sub(milestone.period);
    
        milestone.period = period;
        milestone.bonus = bonus;
    
        totalPeriod = totalPeriod.add(period);
      }
    
      function insertMilestone(uint8 numberAfter, uint period, uint bonus) public onlyOwner {
        require(numberAfter < milestones.length);
    
        totalPeriod = totalPeriod.add(period);
    
        milestones.length++;
    
        for (uint i = milestones.length - 2; i > numberAfter; i--) {
          milestones[i + 1] = milestones[i];
        }
    
        milestones[numberAfter + 1] = Milestone(period, bonus);
      }
    
      function clearMilestones() public onlyOwner {
        require(milestones.length > 0);
        for (uint i = 0; i < milestones.length; i++) {
          delete milestones[i];
        }
        milestones.length -= milestones.length;
        totalPeriod = 0;
      }
    
      function lastSaleDate(uint start) public view returns(uint) {
        return start + totalPeriod * 1 days;
      }
    
      function currentMilestone(uint start) public view returns(uint) {
        uint previousDate = start;
        for(uint i=0; i < milestones.length; i++) {
          if(now >= previousDate && now < previousDate + milestones[i].period * 1 days) {
            return i;
          }
          previousDate = previousDate.add(milestones[i].period * 1 days);
        }
        revert();
      }
    
    }
    
    // File: contracts/ValueBonusFeature.sol
    
    contract ValueBonusFeature is PercentRateProvider {
    
      using SafeMath for uint;
    
      bool public activeValueBonus = true;
    
      struct ValueBonus {
        uint from;
        uint bonus;
      }
    
      ValueBonus[] public valueBonuses;
    
      modifier checkPrevBonus(uint number, uint from, uint bonus) {
        if(number > 0 && number < valueBonuses.length) {
          ValueBonus storage valueBonus = valueBonuses[number - 1];
          require(valueBonus.from < from && valueBonus.bonus < bonus);
        }
        _;
      }
    
      modifier checkNextBonus(uint number, uint from, uint bonus) {
        if(number + 1 < valueBonuses.length) {
          ValueBonus storage valueBonus = valueBonuses[number + 1];
          require(valueBonus.from > from && valueBonus.bonus > bonus);
        }
        _;
      }
    
      function setActiveValueBonus(bool newActiveValueBonus) public onlyOwner {
        activeValueBonus = newActiveValueBonus;
      }
    
      function addValueBonus(uint from, uint bonus) public onlyOwner checkPrevBonus(valueBonuses.length - 1, from, bonus) {
        valueBonuses.push(ValueBonus(from, bonus));
      }
    
      function getValueBonusTokens(uint tokens, uint invested) public view returns(uint) {
        uint valueBonus = getValueBonus(invested);
        if(valueBonus == 0) {
          return 0;
        }
        return tokens.mul(valueBonus).div(percentRate);
      }
    
      function getValueBonus(uint value) public view returns(uint) {
        uint bonus = 0;
        if(activeValueBonus) {
          for(uint i = 0; i < valueBonuses.length; i++) {
            if(value >= valueBonuses[i].from) {
              bonus = valueBonuses[i].bonus;
            } else {
              return bonus;
            }
          }
        }
        return bonus;
      }
    
      function removeValueBonus(uint8 number) public onlyOwner {
        require(number < valueBonuses.length);
    
        delete valueBonuses[number];
    
        for (uint i = number; i < valueBonuses.length - 1; i++) {
          valueBonuses[i] = valueBonuses[i+1];
        }
    
        valueBonuses.length--;
      }
    
      function changeValueBonus(uint8 number, uint from, uint bonus) public onlyOwner checkPrevBonus(number, from, bonus) checkNextBonus(number, from, bonus) {
        require(number < valueBonuses.length);
        ValueBonus storage valueBonus = valueBonuses[number];
        valueBonus.from = from;
        valueBonus.bonus = bonus;
      }
    
      function insertValueBonus(uint8 numberAfter, uint from, uint bonus) public onlyOwner checkPrevBonus(numberAfter, from, bonus) checkNextBonus(numberAfter, from, bonus) {
        require(numberAfter < valueBonuses.length);
    
        valueBonuses.length++;
    
        for (uint i = valueBonuses.length - 2; i > numberAfter; i--) {
          valueBonuses[i + 1] = valueBonuses[i];
        }
    
        valueBonuses[numberAfter + 1] = ValueBonus(from, bonus);
      }
    
      function clearValueBonuses() public onlyOwner {
        require(valueBonuses.length > 0);
        for (uint i = 0; i < valueBonuses.length; i++) {
          delete valueBonuses[i];
        }
        valueBonuses.length = 0;
      }
    
    }
    
    // File: contracts/ICO.sol
    
    contract ICO is ValueBonusFeature, StagedCrowdsale, CommonSale {
    
      FreezeTokensWallet public teamTokensWallet;
    
      address public bountyTokensWallet;
    
      address public reservedTokensWallet;
    
      uint public teamTokensPercent;
    
      uint public bountyTokensPercent;
    
      uint public reservedTokensPercent;
    
      function setTeamTokensPercent(uint newTeamTokensPercent) public onlyOwner {
        teamTokensPercent = newTeamTokensPercent;
      }
    
      function setBountyTokensPercent(uint newBountyTokensPercent) public onlyOwner {
        bountyTokensPercent = newBountyTokensPercent;
      }
    
      function setReservedTokensPercent(uint newReservedTokensPercent) public onlyOwner {
        reservedTokensPercent = newReservedTokensPercent;
      }
    
      function setTeamTokensWallet(address newTeamTokensWallet) public onlyOwner {
        teamTokensWallet = FreezeTokensWallet(newTeamTokensWallet);
      }
    
      function setBountyTokensWallet(address newBountyTokensWallet) public onlyOwner {
        bountyTokensWallet = newBountyTokensWallet;
      }
    
      function setReservedTokensWallet(address newReservedTokensWallet) public onlyOwner {
        reservedTokensWallet = newReservedTokensWallet;
      }
    
      function calculateTokens(uint _invested) internal returns(uint) {
        uint milestoneIndex = currentMilestone(start);
        Milestone storage milestone = milestones[milestoneIndex];
        uint tokens = _invested.mul(price).div(1 ether);
        uint valueBonusTokens = getValueBonusTokens(tokens, _invested);
        if(milestone.bonus > 0) {
          tokens = tokens.add(tokens.mul(milestone.bonus).div(percentRate));
        }
        return tokens.add(valueBonusTokens);
      }
    
      function finish() public onlyOwner {
        uint summaryTokensPercent = bountyTokensPercent.add(teamTokensPercent).add(reservedTokensPercent);
        uint mintedTokens = token.totalSupply();
        uint allTokens = mintedTokens.mul(percentRate).div(percentRate.sub(summaryTokensPercent));
        uint foundersTokens = allTokens.mul(teamTokensPercent).div(percentRate);
        uint bountyTokens = allTokens.mul(bountyTokensPercent).div(percentRate);
        uint reservedTokens = allTokens.mul(reservedTokensPercent).div(percentRate);
        mintTokens(teamTokensWallet, foundersTokens);
        mintTokens(bountyTokensWallet, bountyTokens);
        mintTokens(reservedTokensWallet, reservedTokens);
        token.finishMinting();
        teamTokensWallet.start();
        teamTokensWallet.transferOwnership(owner);
      }
    
      function endSaleDate() public view returns(uint) {
        return lastSaleDate(start);
      }
    
    }
    
    // File: contracts/NextSaleAgentFeature.sol
    
    contract NextSaleAgentFeature is Ownable {
    
      address public nextSaleAgent;
    
      function setNextSaleAgent(address newNextSaleAgent) public onlyOwner {
        nextSaleAgent = newNextSaleAgent;
      }
    
    }
    
    // File: contracts/PreICO.sol
    
    contract PreICO is NextSaleAgentFeature, CommonSale {
    
      uint public period;
    
      function calculateTokens(uint _invested) internal returns(uint) {
        return _invested.mul(price).div(1 ether);
      }
    
      function setPeriod(uint newPeriod) public onlyOwner {
        period = newPeriod;
      }
    
      function finish() public onlyOwner {
        token.setSaleAgent(nextSaleAgent);
      }
    
      function endSaleDate() public view returns(uint) {
        return start.add(period * 1 days);
      }
      
      function fallback() internal minInvestLimited(msg.value) returns(uint) {
        require(now >= start && now < endSaleDate());
        wallet.transfer(msg.value);
        return mintTokensByETH(msg.sender, msg.value);
      }
      
    }
    
    // File: contracts/ReceivingContractCallback.sol
    
    contract ReceivingContractCallback {
    
      function tokenFallback(address _from, uint _value) public;
    
    }
    
    // File: contracts/UBCoinToken.sol
    
    contract UBCoinToken is MintableToken {
    
      string public constant name = "UBCoin";
    
      string public constant symbol = "UBC";
    
      uint32 public constant decimals = 18;
    
      mapping(address => bool)  public registeredCallbacks;
    
      function transfer(address _to, uint256 _value) public returns (bool) {
        return processCallback(super.transfer(_to, _value), msg.sender, _to, _value);
      }
    
      function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        return processCallback(super.transferFrom(_from, _to, _value), _from, _to, _value);
      }
    
      function registerCallback(address callback) public onlyOwner {
        registeredCallbacks[callback] = true;
      }
    
      function deregisterCallback(address callback) public onlyOwner {
        registeredCallbacks[callback] = false;
      }
    
      function processCallback(bool result, address from, address to, uint value) internal returns(bool) {
        if (result && registeredCallbacks[to]) {
          ReceivingContractCallback targetCallback = ReceivingContractCallback(to);
          targetCallback.tokenFallback(from, value);
        }
        return result;
      }
    
    }
    
    // File: contracts/MigrationConfigurator.sol
    
    /**
     * How to migrate:
     * 1. deploy
     * 2. call deploy
     * 3. token.setSaleAgent(new ito)
     */
    contract MigrationConfigurator is Ownable {
    
      MintableToken public token = MintableToken(0x2D3E7D4870a51b918919E7B851FE19983E4c38d5);
    
      ICO public ico;
    
      FreezeTokensWallet public teamTokensWallet;
    
      function setToken(address newAddress) public onlyOwner {
        token = MintableToken(newAddress);
      }
    
      function deploy() public onlyOwner {
        ico = new ICO();
    
        ico.addMilestone(20, 40);
        ico.addMilestone(20, 25);
        ico.addMilestone(20, 20);
        ico.addMilestone(20, 15);
        ico.addMilestone(20, 8);
        ico.addMilestone(4, 0);
        ico.addValueBonus(20000000000000000000,50);
        ico.addValueBonus(50000000000000000000,65);
        ico.addValueBonus(300000000000000000000,80);
        ico.setMinInvestedLimit(100000000000000000);
        ico.setToken(token);
        ico.setPrice(14286000000000000000000);
        ico.setWallet(0x5FB78D8B8f1161731BC80eF93CBcfccc5783356F);
        ico.setBountyTokensWallet(0xdAA156b6eA6b9737eA20c68Db4040B1182E487B6);
        ico.setReservedTokensWallet(0xE1D1898660469797B22D348Ff67d54643d848295);
        ico.setStart(1522627200); // 02 Apr 2018 00:00:00 GMT
        ico.setHardcap(96000000000000000000000);
        ico.setTeamTokensPercent(12);
        ico.setBountyTokensPercent(4);
        ico.setReservedTokensPercent(34);
    
        teamTokensWallet = new FreezeTokensWallet();
        teamTokensWallet.setStartLockPeriod(180);
        teamTokensWallet.setPeriod(360);
        teamTokensWallet.setDuration(90);
        teamTokensWallet.setToken(token);
        teamTokensWallet.transferOwnership(ico);
    
        ico.setTeamTokensWallet(teamTokensWallet);
    
        address manager = 0xF1f94bAD54C8827C3B53754ad7dAa0FF5DCD527d;
    
        ico.transferOwnership(manager);
      }
    
    }

    File 2 of 2: UBCoinToken
    pragma solidity ^0.4.18;
    
    // File: contracts/ownership/Ownable.sol
    
    /**
     * @title Ownable
     * @dev The Ownable contract has an owner address, and provides basic authorization control
     * functions, this simplifies the implementation of "user permissions".
     */
    contract Ownable {
      address public owner;
    
    
      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    
      /**
       * @dev The Ownable constructor sets the original `owner` of the contract to the sender
       * account.
       */
      function Ownable() public {
        owner = msg.sender;
      }
    
    
      /**
       * @dev Throws if called by any account other than the owner.
       */
      modifier onlyOwner() {
        require(msg.sender == owner);
        _;
      }
    
    
      /**
       * @dev Allows the current owner to transfer control of the contract to a newOwner.
       * @param newOwner The address to transfer ownership to.
       */
      function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0));
        OwnershipTransferred(owner, newOwner);
        owner = newOwner;
      }
    
    }
    
    // File: contracts/math/SafeMath.sol
    
    /**
     * @title SafeMath
     * @dev Math operations with safety checks that throw on error
     */
    library SafeMath {
      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
          return 0;
        }
        uint256 c = a * b;
        assert(c / a == b);
        return c;
      }
    
      function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // assert(b > 0); // Solidity automatically throws when dividing by 0
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return c;
      }
    
      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
      }
    
      function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
      }
    }
    
    // File: contracts/token/ERC20Basic.sol
    
    /**
     * @title ERC20Basic
     * @dev Simpler version of ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/179
     */
    contract ERC20Basic {
      uint256 public totalSupply;
      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);
    }
    
    // File: contracts/token/BasicToken.sol
    
    /**
     * @title Basic token
     * @dev Basic version of StandardToken, with no allowances.
     */
    contract BasicToken is ERC20Basic {
      using SafeMath for uint256;
    
      mapping(address => uint256) balances;
    
      /**
      * @dev transfer token for a specified address
      * @param _to The address to transfer to.
      * @param _value The amount to be transferred.
      */
      function transfer(address _to, uint256 _value) public returns (bool) {
        require(_to != address(0));
        require(_value <= balances[msg.sender]);
    
        // 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;
      }
    
      /**
      * @dev Gets the balance of the specified address.
      * @param _owner The address to query the the balance of.
      * @return An uint256 representing the amount owned by the passed address.
      */
      function balanceOf(address _owner) public view returns (uint256 balance) {
        return balances[_owner];
      }
    
    }
    
    // File: contracts/token/ERC20.sol
    
    /**
     * @title ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    contract ERC20 is ERC20Basic {
      function allowance(address owner, address spender) public view returns (uint256);
      function transferFrom(address from, address to, uint256 value) public returns (bool);
      function approve(address spender, uint256 value) public returns (bool);
      event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    
    // File: contracts/token/StandardToken.sol
    
    /**
     * @title Standard ERC20 token
     *
     * @dev Implementation of the basic standard token.
     * @dev https://github.com/ethereum/EIPs/issues/20
     * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
     */
    contract StandardToken is ERC20, BasicToken {
    
      mapping (address => mapping (address => uint256)) internal allowed;
    
    
      /**
       * @dev Transfer tokens from one address to another
       * @param _from address The address which you want to send tokens from
       * @param _to address The address which you want to transfer to
       * @param _value uint256 the amount of tokens to be transferred
       */
      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].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        Transfer(_from, _to, _value);
        return true;
      }
    
      /**
       * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
       *
       * Beware that changing an allowance with this method brings the risk that someone may use both the old
       * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
       * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
       * @param _spender The address which will spend the funds.
       * @param _value The amount of tokens to be spent.
       */
      function approve(address _spender, uint256 _value) public returns (bool) {
        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
      }
    
      /**
       * @dev Function to check the amount of tokens that an owner allowed to a spender.
       * @param _owner address The address which owns the funds.
       * @param _spender address The address which will spend the funds.
       * @return A uint256 specifying the amount of tokens still available for the spender.
       */
      function allowance(address _owner, address _spender) public view returns (uint256) {
        return allowed[_owner][_spender];
      }
    
      /**
       * @dev Increase the amount of tokens that an owner allowed to a spender.
       *
       * approve should be called when allowed[_spender] == 0. To increment
       * allowed value is better to use this function to avoid 2 calls (and wait until
       * the first transaction is mined)
       * From MonolithDAO Token.sol
       * @param _spender The address which will spend the funds.
       * @param _addedValue The amount of tokens to increase the allowance by.
       */
      function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
        allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
        Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
      }
    
      /**
       * @dev Decrease the amount of tokens that an owner allowed to a spender.
       *
       * approve should be called when allowed[_spender] == 0. To decrement
       * allowed value is better to use this function to avoid 2 calls (and wait until
       * the first transaction is mined)
       * From MonolithDAO Token.sol
       * @param _spender The address which will spend the funds.
       * @param _subtractedValue The amount of tokens to decrease the allowance by.
       */
      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.sub(_subtractedValue);
        }
        Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
      }
    
    }
    
    // File: contracts/MintableToken.sol
    
    contract MintableToken is StandardToken, Ownable {
    
      event Mint(address indexed to, uint256 amount);
    
      event MintFinished();
    
      bool public mintingFinished = false;
    
      address public saleAgent;
    
      modifier notLocked() {
        require(msg.sender == owner || msg.sender == saleAgent || mintingFinished);
        _;
      }
    
      function setSaleAgent(address newSaleAgnet) public {
        require(msg.sender == saleAgent || msg.sender == owner);
        saleAgent = newSaleAgnet;
      }
    
      function mint(address _to, uint256 _amount) public returns (bool) {
        require((msg.sender == saleAgent || msg.sender == owner) && !mintingFinished);
        
        totalSupply = totalSupply.add(_amount);
        balances[_to] = balances[_to].add(_amount);
        Mint(_to, _amount);
        return true;
      }
    
      /**
       * @dev Function to stop minting new tokens.
       * @return True if the operation was successful.
       */
      function finishMinting() public returns (bool) {
        require((msg.sender == saleAgent || msg.sender == owner) && !mintingFinished);
        mintingFinished = true;
        MintFinished();
        return true;
      }
    
      function transfer(address _to, uint256 _value) public notLocked returns (bool) {
        return super.transfer(_to, _value);
      }
    
      function transferFrom(address from, address to, uint256 value) public notLocked returns (bool) {
        return super.transferFrom(from, to, value);
      }
    
    }
    
    // File: contracts/FreezeTokensWallet.sol
    
    contract FreezeTokensWallet is Ownable {
    
      using SafeMath for uint256;
    
      MintableToken public token;
    
      bool public started;
    
      uint public startLockPeriod = 180 days;
    
      uint public period = 360 days;
    
      uint public duration = 90 days;
    
      uint public startUnlock;
    
      uint public retrievedTokens;
    
      uint public startBalance;
    
      modifier notStarted() {
        require(!started);
        _;
      }
    
      function setPeriod(uint newPeriod) public onlyOwner notStarted {
        period = newPeriod * 1 days;
      }
    
      function setDuration(uint newDuration) public onlyOwner notStarted {
        duration = newDuration * 1 days;
      }
    
      function setStartLockPeriod(uint newStartLockPeriod) public onlyOwner notStarted {
        startLockPeriod = newStartLockPeriod * 1 days;
      }
    
      function setToken(address newToken) public onlyOwner notStarted {
        token = MintableToken(newToken);
      }
    
      function start() public onlyOwner notStarted {
        startUnlock = now + startLockPeriod;
        retrievedTokens = 0;
        startBalance = token.balanceOf(this);
        started = true;
      }
    
      function retrieveTokens(address to) public onlyOwner {
        require(started && now >= startUnlock);
        if (now >= startUnlock + period) {
          token.transfer(to, token.balanceOf(this));
        } else {
          uint parts = period.div(duration);
          uint tokensByPart = startBalance.div(parts);
          uint timeSinceStart = now.sub(startUnlock);
          uint pastParts = timeSinceStart.div(duration);
          uint tokensToRetrieveSinceStart = pastParts.mul(tokensByPart);
          uint tokensToRetrieve = tokensToRetrieveSinceStart.sub(retrievedTokens);
          if(tokensToRetrieve > 0) {
            retrievedTokens = retrievedTokens.add(tokensToRetrieve);
            token.transfer(to, tokensToRetrieve);
          }
        }
      }
    }
    
    // File: contracts/InvestedProvider.sol
    
    contract InvestedProvider is Ownable {
    
      uint public invested;
    
    }
    
    // File: contracts/PercentRateProvider.sol
    
    contract PercentRateProvider is Ownable {
    
      uint public percentRate = 100;
    
      function setPercentRate(uint newPercentRate) public onlyOwner {
        percentRate = newPercentRate;
      }
    
    }
    
    // File: contracts/RetrieveTokensFeature.sol
    
    contract RetrieveTokensFeature is Ownable {
    
      function retrieveTokens(address to, address anotherToken) public onlyOwner {
        ERC20 alienToken = ERC20(anotherToken);
        alienToken.transfer(to, alienToken.balanceOf(this));
      }
    
    }
    
    // File: contracts/WalletProvider.sol
    
    contract WalletProvider is Ownable {
    
      address public wallet;
    
      function setWallet(address newWallet) public onlyOwner {
        wallet = newWallet;
      }
    
    }
    
    // File: contracts/CommonSale.sol
    
    contract CommonSale is InvestedProvider, WalletProvider, PercentRateProvider, RetrieveTokensFeature {
    
      using SafeMath for uint;
    
      address public directMintAgent;
    
      uint public price;
    
      uint public start;
    
      uint public minInvestedLimit;
    
      MintableToken public token;
    
      uint public hardcap;
    
      modifier isUnderHardcap() {
        require(invested < hardcap);
        _;
      }
    
      function setHardcap(uint newHardcap) public onlyOwner {
        hardcap = newHardcap;
      }
    
      modifier onlyDirectMintAgentOrOwner() {
        require(directMintAgent == msg.sender || owner == msg.sender);
        _;
      }
    
      modifier minInvestLimited(uint value) {
        require(value >= minInvestedLimit);
        _;
      }
    
      function setStart(uint newStart) public onlyOwner {
        start = newStart;
      }
    
      function setMinInvestedLimit(uint newMinInvestedLimit) public onlyOwner {
        minInvestedLimit = newMinInvestedLimit;
      }
    
      function setDirectMintAgent(address newDirectMintAgent) public onlyOwner {
        directMintAgent = newDirectMintAgent;
      }
    
      function setPrice(uint newPrice) public onlyOwner {
        price = newPrice;
      }
    
      function setToken(address newToken) public onlyOwner {
        token = MintableToken(newToken);
      }
    
      function calculateTokens(uint _invested) internal returns(uint);
    
      function mintTokensExternal(address to, uint tokens) public onlyDirectMintAgentOrOwner {
        mintTokens(to, tokens);
      }
    
      function mintTokens(address to, uint tokens) internal {
        token.mint(this, tokens);
        token.transfer(to, tokens);
      }
    
      function endSaleDate() public view returns(uint);
    
      function mintTokensByETHExternal(address to, uint _invested) public onlyDirectMintAgentOrOwner returns(uint) {
        return mintTokensByETH(to, _invested);
      }
    
      function mintTokensByETH(address to, uint _invested) internal isUnderHardcap returns(uint) {
        invested = invested.add(_invested);
        uint tokens = calculateTokens(_invested);
        mintTokens(to, tokens);
        return tokens;
      }
    
      function fallback() internal minInvestLimited(msg.value) returns(uint) {
        require(now >= start && now < endSaleDate());
        wallet.transfer(msg.value);
        return mintTokensByETH(msg.sender, msg.value);
      }
    
      function () public payable {
        fallback();
      }
    
    }
    
    // File: contracts/StagedCrowdsale.sol
    
    contract StagedCrowdsale is Ownable {
    
      using SafeMath for uint;
    
      struct Milestone {
        uint period;
        uint bonus;
      }
    
      uint public totalPeriod;
    
      Milestone[] public milestones;
    
      function milestonesCount() public view returns(uint) {
        return milestones.length;
      }
    
      function addMilestone(uint period, uint bonus) public onlyOwner {
        require(period > 0);
        milestones.push(Milestone(period, bonus));
        totalPeriod = totalPeriod.add(period);
      }
    
      function removeMilestone(uint8 number) public onlyOwner {
        require(number < milestones.length);
        Milestone storage milestone = milestones[number];
        totalPeriod = totalPeriod.sub(milestone.period);
    
        delete milestones[number];
    
        for (uint i = number; i < milestones.length - 1; i++) {
          milestones[i] = milestones[i+1];
        }
    
        milestones.length--;
      }
    
      function changeMilestone(uint8 number, uint period, uint bonus) public onlyOwner {
        require(number < milestones.length);
        Milestone storage milestone = milestones[number];
    
        totalPeriod = totalPeriod.sub(milestone.period);
    
        milestone.period = period;
        milestone.bonus = bonus;
    
        totalPeriod = totalPeriod.add(period);
      }
    
      function insertMilestone(uint8 numberAfter, uint period, uint bonus) public onlyOwner {
        require(numberAfter < milestones.length);
    
        totalPeriod = totalPeriod.add(period);
    
        milestones.length++;
    
        for (uint i = milestones.length - 2; i > numberAfter; i--) {
          milestones[i + 1] = milestones[i];
        }
    
        milestones[numberAfter + 1] = Milestone(period, bonus);
      }
    
      function clearMilestones() public onlyOwner {
        require(milestones.length > 0);
        for (uint i = 0; i < milestones.length; i++) {
          delete milestones[i];
        }
        milestones.length -= milestones.length;
        totalPeriod = 0;
      }
    
      function lastSaleDate(uint start) public view returns(uint) {
        return start + totalPeriod * 1 days;
      }
    
      function currentMilestone(uint start) public view returns(uint) {
        uint previousDate = start;
        for(uint i=0; i < milestones.length; i++) {
          if(now >= previousDate && now < previousDate + milestones[i].period * 1 days) {
            return i;
          }
          previousDate = previousDate.add(milestones[i].period * 1 days);
        }
        revert();
      }
    
    }
    
    // File: contracts/ICO.sol
    
    contract ICO is StagedCrowdsale, CommonSale {
    
      FreezeTokensWallet public teamTokensWallet;
    
      address public bountyTokensWallet;
    
      address public reservedTokensWallet;
    
      uint public teamTokensPercent;
    
      uint public bountyTokensPercent;
    
      uint public reservedTokensPercent;
    
      function setTeamTokensPercent(uint newTeamTokensPercent) public onlyOwner {
        teamTokensPercent = newTeamTokensPercent;
      }
    
      function setBountyTokensPercent(uint newBountyTokensPercent) public onlyOwner {
        bountyTokensPercent = newBountyTokensPercent;
      }
    
      function setReservedTokensPercent(uint newReservedTokensPercent) public onlyOwner {
        reservedTokensPercent = newReservedTokensPercent;
      }
    
      function setTeamTokensWallet(address newTeamTokensWallet) public onlyOwner {
        teamTokensWallet = FreezeTokensWallet(newTeamTokensWallet);
      }
    
      function setBountyTokensWallet(address newBountyTokensWallet) public onlyOwner {
        bountyTokensWallet = newBountyTokensWallet;
      }
    
      function setReservedTokensWallet(address newReservedTokensWallet) public onlyOwner {
        reservedTokensWallet = newReservedTokensWallet;
      }
    
      function calculateTokens(uint _invested) internal returns(uint) {
        uint milestoneIndex = currentMilestone(start);
        Milestone storage milestone = milestones[milestoneIndex];
    
        uint tokens = _invested.mul(price).div(1 ether);
        if(milestone.bonus > 0) {
          tokens = tokens.add(tokens.mul(milestone.bonus).div(percentRate));
        }
        return tokens;
      }
    
      function finish() public onlyOwner {
        uint summaryTokensPercent = bountyTokensPercent.add(teamTokensPercent).add(reservedTokensPercent);
        uint mintedTokens = token.totalSupply();
        uint allTokens = mintedTokens.mul(percentRate).div(percentRate.sub(summaryTokensPercent));
        uint foundersTokens = allTokens.mul(teamTokensPercent).div(percentRate);
        uint bountyTokens = allTokens.mul(bountyTokensPercent).div(percentRate);
        uint reservedTokens = allTokens.mul(reservedTokensPercent).div(percentRate);
        mintTokens(teamTokensWallet, foundersTokens);
        mintTokens(bountyTokensWallet, bountyTokens);
        mintTokens(reservedTokensWallet, reservedTokens);
        token.finishMinting();
        teamTokensWallet.start();
        teamTokensWallet.transferOwnership(owner);
      }
    
      function endSaleDate() public view returns(uint) {
        return lastSaleDate(start);
      }
    
    }
    
    // File: contracts/NextSaleAgentFeature.sol
    
    contract NextSaleAgentFeature is Ownable {
    
      address public nextSaleAgent;
    
      function setNextSaleAgent(address newNextSaleAgent) public onlyOwner {
        nextSaleAgent = newNextSaleAgent;
      }
    
    }
    
    // File: contracts/WhiteListFeature.sol
    
    contract WhiteListFeature is CommonSale {
    
      mapping(address => bool)  public whiteList;
    
      function addToWhiteList(address _address) public onlyDirectMintAgentOrOwner {
        whiteList[_address] = true;
      }
    
      function deleteFromWhiteList(address _address) public onlyDirectMintAgentOrOwner {
        whiteList[_address] = false;
      }
    
    }
    
    // File: contracts/PreICO.sol
    
    contract PreICO is NextSaleAgentFeature, WhiteListFeature {
    
      uint public period;
    
      function calculateTokens(uint _invested) internal returns(uint) {
        return _invested.mul(price).div(1 ether);
      }
    
      function setPeriod(uint newPeriod) public onlyOwner {
        period = newPeriod;
      }
    
      function finish() public onlyOwner {
        token.setSaleAgent(nextSaleAgent);
      }
    
      function endSaleDate() public view returns(uint) {
        return start.add(period * 1 days);
      }
      
      function fallback() internal minInvestLimited(msg.value) returns(uint) {
        require(now >= start && now < endSaleDate());
        require(whiteList[msg.sender]);
        wallet.transfer(msg.value);
        return mintTokensByETH(msg.sender, msg.value);
      }
      
    }
    
    // File: contracts/ReceivingContractCallback.sol
    
    contract ReceivingContractCallback {
    
      function tokenFallback(address _from, uint _value) public;
    
    }
    
    // File: contracts/UBCoinToken.sol
    
    contract UBCoinToken is MintableToken {
    
      string public constant name = "UBCoin";
    
      string public constant symbol = "UBC";
    
      uint32 public constant decimals = 18;
    
      mapping(address => bool)  public registeredCallbacks;
    
      function transfer(address _to, uint256 _value) public returns (bool) {
        return processCallback(super.transfer(_to, _value), msg.sender, _to, _value);
      }
    
      function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        return processCallback(super.transferFrom(_from, _to, _value), _from, _to, _value);
      }
    
      function registerCallback(address callback) public onlyOwner {
        registeredCallbacks[callback] = true;
      }
    
      function deregisterCallback(address callback) public onlyOwner {
        registeredCallbacks[callback] = false;
      }
    
      function processCallback(bool result, address from, address to, uint value) internal returns(bool) {
        if (result && registeredCallbacks[to]) {
          ReceivingContractCallback targetCallback = ReceivingContractCallback(to);
          targetCallback.tokenFallback(from, value);
        }
        return result;
      }
    
    }
    
    // File: contracts/Configurator.sol
    
    contract Configurator is Ownable {
    
      MintableToken public token;
    
      PreICO public preICO;
    
      ICO public ico;
    
      FreezeTokensWallet public teamTokensWallet;
    
      function deploy() public onlyOwner {
    
        token = new UBCoinToken();
    
        preICO = new PreICO();
    
        preICO.setWallet(0x00EE9d057f66754C7D92550F77Aeb0A87AE34B01);
        preICO.setStart(1520640000); // 10 Mar 2018 00:00:00 GMT
        preICO.setPeriod(22);
        preICO.setPrice(33334000000000000000000);
        preICO.setMinInvestedLimit(100000000000000000);
        preICO.setToken(token);
        preICO.setHardcap(8500000000000000000000);
        token.setSaleAgent(preICO);
    
        ico = new ICO();
    
        ico.addMilestone(20, 40);
        ico.addMilestone(20, 20);
        ico.addMilestone(20, 0);
        ico.setMinInvestedLimit(100000000000000000);
        ico.setToken(token);
        ico.setPrice(14286000000000000000000);
        ico.setWallet(0x5FB78D8B8f1161731BC80eF93CBcfccc5783356F);
        ico.setBountyTokensWallet(0xdAA156b6eA6b9737eA20c68Db4040B1182E487B6);
        ico.setReservedTokensWallet(0xE1D1898660469797B22D348Ff67d54643d848295);
        ico.setStart(1522627200); // 02 Apr 2018 00:00:00 GMT
        ico.setHardcap(96000000000000000000000);
        ico.setTeamTokensPercent(12);
        ico.setBountyTokensPercent(4);
        ico.setReservedTokensPercent(34);
    
        teamTokensWallet = new FreezeTokensWallet();
        teamTokensWallet.setStartLockPeriod(180);
        teamTokensWallet.setPeriod(360);
        teamTokensWallet.setDuration(90);
        teamTokensWallet.setToken(token);
        teamTokensWallet.transferOwnership(ico);
    
        ico.setTeamTokensWallet(teamTokensWallet);
    
        preICO.setNextSaleAgent(ico);
    
        address manager = 0xF1f94bAD54C8827C3B53754ad7dAa0FF5DCD527d;
    
        token.transferOwnership(manager);
        preICO.transferOwnership(manager);
        ico.transferOwnership(manager);
      }
    
    }