ETH Price: $1,995.24 (+0.24%)

Transaction Decoder

Block:
4631571 at Nov-27-2017 12:04:38 PM +UTC
Transaction Fee:
0.00624552 ETH $12.46
Gas Used:
208,184 Gas / 30 Gwei

Emitted Events:

5 AngelToken.MintEvent( account=[Sender] 0xa065ac0348e83e0e0873937b03b2b788c79ae5ee, value=18181818181818181818181 )
6 AngelToken.Transfer( from=0x0000000000000000000000000000000000000000, to=[Sender] 0xa065ac0348e83e0e0873937b03b2b788c79ae5ee, value=18181818181818181818181 )
7 AngelToken.MintEvent( account=0x2b0556a6298eA3D35E90F1df32cc126b31F59770, value=7792207792207792207791 )
8 AngelToken.Transfer( from=0x0000000000000000000000000000000000000000, to=0x2b0556a6298eA3D35E90F1df32cc126b31F59770, value=7792207792207792207791 )
9 AngelCentralBank.InvestmentEvent( investor=[Sender] 0xa065ac0348e83e0e0873937b03b2b788c79ae5ee, eth=2000000000000000000, angel=18181818181818181818181 )

Account State Difference:

  Address   Before After State Difference Code
0x2b0556a6...b31F59770 22.601258101142187538 Eth23.001258101142187538 Eth0.4
0xa065Ac03...8c79aE5ee
7.054031833 Eth
Nonce: 11
5.047786313 Eth
Nonce: 12
2.00624552
0xc499eA94...c4Dbe21aa
0xE1f243A9...276574294
(AngelToken: Token Sale)
90.405032404568750154 Eth92.005032404568750154 Eth1.6
(xnpool.cn)
57.151984619945553829 Eth57.158230139945553829 Eth0.00624552

Execution Trace

ETH 2 AngelCentralBank.CALL( )
  • AngelToken.mint( _account=0xa065Ac0348E83E0E0873937b03b2b788c79aE5ee, _value=18181818181818181818181 )
  • AngelToken.mint( _account=0x2b0556a6298eA3D35E90F1df32cc126b31F59770, _value=7792207792207792207791 )
  • ETH 0.4 0x2b0556a6298ea3d35e90f1df32cc126b31f59770.CALL( )
    File 1 of 2: AngelCentralBank
    pragma solidity ^0.4.16;
    
    
    /**
     * @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;
    
    
      /**
       * @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) external onlyOwner {
        if (newOwner != address(0)) {
          owner = newOwner;
        }
      }
    
    }
    
    
    
    /**
     * @title SafeMath
     * @dev Math operations with safety checks that throw on error
     */
    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) {
        // 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 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;
      }
    }
    
    
    
    /**
     * @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 constant returns (uint256);
      function transfer(address to, uint256 value) public returns (bool);
      event Transfer(address indexed from, address indexed to, uint256 value);
    }
    
    
    
    /**
     * @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) {
        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 constant returns (uint256 balance) {
        return balances[_owner];
      }
    
    }
    
    
    
    /**
     * @title ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    contract ERC20 is ERC20Basic {
      function allowance(address owner, address spender) public constant 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);
    }
    
    
    
    /**
     * @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)) 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 amout of tokens to be transfered
       */
      function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        var _allowance = allowed[_from][msg.sender];
    
        // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
        // require (_value <= _allowance);
    
        balances[_to] = balances[_to].add(_value);
        balances[_from] = balances[_from].sub(_value);
        allowed[_from][msg.sender] = _allowance.sub(_value);
        Transfer(_from, _to, _value);
        return true;
      }
    
      /**
       * @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender.
       * @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) {
    
        // To change the approve amount you first have to reduce the addresses`
        //  allowance to zero by calling `approve(_spender, 0)` if it is not
        //  already 0 to mitigate the race condition described here:
        //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        require((_value == 0) || (allowed[msg.sender][_spender] == 0));
    
        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 specifing the amount of tokens still avaible for the spender.
       */
      function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
        return allowed[_owner][_spender];
      }
    
    }
    
    
    
    /**
     * @title Manageable
     * @dev Contract that allows to grant permissions to any address
     * @dev In real life we are no able to perform all actions with just one Ethereum address
     * @dev because risks are too high.
     * @dev Instead owner delegates rights to manage an contract to the different addresses and
     * @dev stay able to revoke permissions at any time.
     */
    contract Manageable is Ownable {
    
      /* Storage */
    
      mapping (address => bool) managerEnabled;  // hard switch for a manager - on/off
      mapping (address => mapping (string => bool)) managerPermissions;  // detailed info about manager`s permissions
    
    
      /* Events */
    
      event ManagerEnabledEvent(address indexed manager);
      event ManagerDisabledEvent(address indexed manager);
      event ManagerPermissionGrantedEvent(address indexed manager, string permission);
      event ManagerPermissionRevokedEvent(address indexed manager, string permission);
    
    
      /* Configure contract */
    
      /**
       * @dev Function to add new manager
       * @param _manager address New manager
       */
      function enableManager(address _manager) external onlyOwner onlyValidAddress(_manager) {
        require(managerEnabled[_manager] == false);
    
        managerEnabled[_manager] = true;
        ManagerEnabledEvent(_manager);
      }
    
      /**
       * @dev Function to remove existing manager
       * @param _manager address Existing manager
       */
      function disableManager(address _manager) external onlyOwner onlyValidAddress(_manager) {
        require(managerEnabled[_manager] == true);
    
        managerEnabled[_manager] = false;
        ManagerDisabledEvent(_manager);
      }
    
      /**
       * @dev Function to grant new permission to the manager
       * @param _manager        address Existing manager
       * @param _permissionName string  Granted permission name
       */
      function grantManagerPermission(
        address _manager, string _permissionName
      )
        external
        onlyOwner
        onlyValidAddress(_manager)
        onlyValidPermissionName(_permissionName)
      {
        require(managerPermissions[_manager][_permissionName] == false);
    
        managerPermissions[_manager][_permissionName] = true;
        ManagerPermissionGrantedEvent(_manager, _permissionName);
      }
    
      /**
       * @dev Function to revoke permission of the manager
       * @param _manager        address Existing manager
       * @param _permissionName string  Revoked permission name
       */
      function revokeManagerPermission(
        address _manager, string _permissionName
      )
        external
        onlyOwner
        onlyValidAddress(_manager)
        onlyValidPermissionName(_permissionName)
      {
        require(managerPermissions[_manager][_permissionName] == true);
    
        managerPermissions[_manager][_permissionName] = false;
        ManagerPermissionRevokedEvent(_manager, _permissionName);
      }
    
    
      /* Getters */
    
      /**
       * @dev Function to check manager status
       * @param _manager address Manager`s address
       * @return True if manager is enabled
       */
      function isManagerEnabled(address _manager) public constant onlyValidAddress(_manager) returns (bool) {
        return managerEnabled[_manager];
      }
    
      /**
       * @dev Function to check permissions of a manager
       * @param _manager        address Manager`s address
       * @param _permissionName string  Permission name
       * @return True if manager has been granted needed permission
       */
      function isPermissionGranted(
        address _manager, string _permissionName
      )
        public
        constant
        onlyValidAddress(_manager)
        onlyValidPermissionName(_permissionName)
        returns (bool)
      {
        return managerPermissions[_manager][_permissionName];
      }
    
      /**
       * @dev Function to check if the manager can perform the action or not
       * @param _manager        address Manager`s address
       * @param _permissionName string  Permission name
       * @return True if manager is enabled and has been granted needed permission
       */
      function isManagerAllowed(
        address _manager, string _permissionName
      )
        public
        constant
        onlyValidAddress(_manager)
        onlyValidPermissionName(_permissionName)
        returns (bool)
      {
        return (managerEnabled[_manager] && managerPermissions[_manager][_permissionName]);
      }
    
    
      /* Helpers */
    
      /**
       * @dev Modifier to check manager address
       */
      modifier onlyValidAddress(address _manager) {
        require(_manager != address(0x0));
        _;
      }
    
      /**
       * @dev Modifier to check name of manager permission
       */
      modifier onlyValidPermissionName(string _permissionName) {
        require(bytes(_permissionName).length != 0);
        _;
      }
    
    
      /* Outcome */
    
      /**
       * @dev Modifier to use in derived contracts
       */
      modifier onlyAllowedManager(string _permissionName) {
        require(isManagerAllowed(msg.sender, _permissionName) == true);
        _;
      }
    }
    
    
    
    /**
     * @title Pausable
     * @dev Base contract which allows children to implement an emergency stop mechanism.
     * @dev Based on zeppelin's Pausable, but integrated with Manageable
     * @dev Contract is in paused state by default and should be explicitly unlocked
     */
    contract Pausable is Manageable {
    
      /**
       * Events
       */
    
      event PauseEvent();
      event UnpauseEvent();
    
    
      /**
       * Storage
       */
    
      bool paused = true;
    
    
      /**
       * @dev modifier to allow actions only when the contract IS paused
       */
      modifier whenContractNotPaused() {
        require(paused == false);
        _;
      }
    
      /**
       * @dev modifier to allow actions only when the contract IS NOT paused
       */
      modifier whenContractPaused {
        require(paused == true);
        _;
      }
    
      /**
       * @dev called by the manager to pause, triggers stopped state
       */
      function pauseContract() external onlyAllowedManager('pause_contract') whenContractNotPaused {
        paused = true;
        PauseEvent();
      }
    
      /**
       * @dev called by the manager to unpause, returns to normal state
       */
      function unpauseContract() external onlyAllowedManager('unpause_contract') whenContractPaused {
        paused = false;
        UnpauseEvent();
      }
    
      /**
       * @dev The getter for "paused" contract variable
       */
      function getPaused() external constant returns (bool) {
        return paused;
      }
    }
    
    
    
    /**
     * @title NamedToken
     */
    contract NamedToken {
      string public name;
      string public symbol;
      uint8 public decimals;
    
      function NamedToken(string _name, string _symbol, uint8 _decimals) public {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
      }
    
      /**
       * @dev Function to calculate hash of the token`s name.
       * @dev Function needed because we can not just return name of the token to another contract - strings have variable length
       * @return Hash of the token`s name
       */
      function getNameHash() external constant returns (bytes32 result){
        return keccak256(name);
      }
    
      /**
       * @dev Function to calculate hash of the token`s symbol.
       * @dev Function needed because we can not just return symbol of the token to another contract - strings have variable length
       * @return Hash of the token`s symbol
       */
      function getSymbolHash() external constant returns (bytes32 result){
        return keccak256(symbol);
      }
    }
    
    
    
    /**
     * @title AngelToken
     */
    contract AngelToken is StandardToken, NamedToken, Pausable {
    
      /* Events */
    
      event MintEvent(address indexed account, uint value);
      event BurnEvent(address indexed account, uint value);
      event SpendingBlockedEvent(address indexed account);
      event SpendingUnblockedEvent(address indexed account);
    
    
      /* Storage */
    
      address public centralBankAddress = 0x0;
      mapping (address => uint) spendingBlocksNumber;
    
    
      /* Constructor */
    
      function AngelToken() public NamedToken('Angel Token', 'ANGL', 18) {
        centralBankAddress = msg.sender;
      }
    
    
      /* Methods */
    
      function transfer(address _to, uint _value) public returns (bool) {
        if (_to != centralBankAddress) {
          require(!paused);
        }
        require(spendingBlocksNumber[msg.sender] == 0);
    
        bool result = super.transfer(_to, _value);
        if (result == true && _to == centralBankAddress) {
          AngelCentralBank(centralBankAddress).angelBurn(msg.sender, _value);
        }
        return result;
      }
    
      function approve(address _spender, uint _value) public whenContractNotPaused returns (bool){
        return super.approve(_spender, _value);
      }
    
      function transferFrom(address _from, address _to, uint _value) public whenContractNotPaused returns (bool){
        require(spendingBlocksNumber[_from] == 0);
    
        bool result = super.transferFrom(_from, _to, _value);
        if (result == true && _to == centralBankAddress) {
          AngelCentralBank(centralBankAddress).angelBurn(_from, _value);
        }
        return result;
      }
    
    
      function mint(address _account, uint _value) external onlyAllowedManager('mint_tokens') {
        balances[_account] = balances[_account].add(_value);
        totalSupply = totalSupply.add(_value);
        MintEvent(_account, _value);
        Transfer(address(0x0), _account, _value); // required for blockexplorers
      }
    
      function burn(uint _value) external onlyAllowedManager('burn_tokens') {
        balances[msg.sender] = balances[msg.sender].sub(_value);
        totalSupply = totalSupply.sub(_value);
        BurnEvent(msg.sender, _value);
      }
    
      function blockSpending(address _account) external onlyAllowedManager('block_spending') {
        spendingBlocksNumber[_account] = spendingBlocksNumber[_account].add(1);
        SpendingBlockedEvent(_account);
      }
    
      function unblockSpending(address _account) external onlyAllowedManager('unblock_spending') {
        spendingBlocksNumber[_account] = spendingBlocksNumber[_account].sub(1);
        SpendingUnblockedEvent(_account);
      }
    }
    
    
    
    /**
     * @title AngelCentralBank
     *
     * @dev Crowdsale and escrow contract
     */
    contract AngelCentralBank {
    
      /* Data structures */
    
      struct InvestmentRecord {
        uint tokensSoldBeforeWei;
        uint investedEthWei;
        uint purchasedTokensWei;
        uint refundedEthWei;
        uint returnedTokensWei;
      }
    
    
      /* Storage - config */
    
      uint public constant icoCap = 70000000 * (10 ** 18);
    
      uint public initialTokenPrice = 1 * (10 ** 18) / (10 ** 4); // means 0.0001 ETH for one token
    
      uint public constant landmarkSize = 1000000 * (10 ** 18);
      uint public constant landmarkPriceStepNumerator = 10;
      uint public constant landmarkPriceStepDenominator = 100;
    
      uint public constant firstRefundRoundRateNumerator = 80;
      uint public constant firstRefundRoundRateDenominator = 100;
      uint public constant secondRefundRoundRateNumerator = 40;
      uint public constant secondRefundRoundRateDenominator = 100;
    
      uint public constant initialFundsReleaseNumerator = 20; // part of investment
      uint public constant initialFundsReleaseDenominator = 100;
      uint public constant afterFirstRefundRoundFundsReleaseNumerator = 50; // part of remaining funds
      uint public constant afterFirstRefundRoundFundsReleaseDenominator = 100;
    
      uint public constant angelFoundationShareNumerator = 30;
      uint public constant angelFoundationShareDenominator = 100;
    
      /* Storage - state */
    
      address public angelFoundationAddress = address(0x2b0556a6298eA3D35E90F1df32cc126b31F59770);
      uint public icoLaunchTimestamp = 1511784000;  // November 27th 12:00 GMT
      uint public icoFinishTimestamp = 1513727999;  // December 19th 23:59:59 GMT
      uint public firstRefundRoundFinishTimestamp = 1520424000;  // March 7th 2018 12:00 GMT
      uint public secondRefundRoundFinishTimestamp = 1524744000;  // April 26th 2018 12:00 GMT
    
    
      AngelToken public angelToken;
    
      mapping (address => InvestmentRecord[]) public investments; // investorAddress => list of investments
      mapping (address => bool) public investors;
      uint public totalInvestors = 0;
      uint public totalTokensSold = 0;
    
      bool isIcoFinished = false;
      bool firstRefundRoundFundsWithdrawal = false;
    
    
      /* Events */
    
      event InvestmentEvent(address indexed investor, uint eth, uint angel);
      event RefundEvent(address indexed investor, uint eth, uint angel);
    
    
      /* Constructor and config */
    
      function AngelCentralBank() public {
        angelToken = new AngelToken();
        angelToken.enableManager(address(this));
        angelToken.grantManagerPermission(address(this), 'mint_tokens');
        angelToken.grantManagerPermission(address(this), 'burn_tokens');
        angelToken.grantManagerPermission(address(this), 'unpause_contract');
        angelToken.transferOwnership(angelFoundationAddress);
      }
    
      /* Investments */
    
      /**
       * @dev Fallback function receives ETH and sends tokens back
       */
      function () public payable {
        angelRaise();
      }
    
      /**
       * @dev Process new ETH investment and sends tokens back
       */
      function angelRaise() internal {
        require(msg.value > 0);
        require(now >= icoLaunchTimestamp && now < icoFinishTimestamp);
    
        // calculate amount of tokens for received ETH
        uint _purchasedTokensWei = 0;
        uint _notProcessedEthWei = 0;
        (_purchasedTokensWei, _notProcessedEthWei) = calculatePurchasedTokens(totalTokensSold, msg.value);
        uint _actualInvestment = (msg.value - _notProcessedEthWei);
    
        // create record for the investment
        uint _newRecordIndex = investments[msg.sender].length;
        investments[msg.sender].length += 1;
        investments[msg.sender][_newRecordIndex].tokensSoldBeforeWei = totalTokensSold;
        investments[msg.sender][_newRecordIndex].investedEthWei = _actualInvestment;
        investments[msg.sender][_newRecordIndex].purchasedTokensWei = _purchasedTokensWei;
        investments[msg.sender][_newRecordIndex].refundedEthWei = 0;
        investments[msg.sender][_newRecordIndex].returnedTokensWei = 0;
    
        // calculate stats
        if (investors[msg.sender] == false) {
          totalInvestors += 1;
        }
        investors[msg.sender] = true;
        totalTokensSold += _purchasedTokensWei;
    
        // transfer tokens and ETH
        angelToken.mint(msg.sender, _purchasedTokensWei);
        angelToken.mint(angelFoundationAddress,
                        _purchasedTokensWei * angelFoundationShareNumerator / (angelFoundationShareDenominator - angelFoundationShareNumerator));
        angelFoundationAddress.transfer(_actualInvestment * initialFundsReleaseNumerator / initialFundsReleaseDenominator);
        if (_notProcessedEthWei > 0) {
          msg.sender.transfer(_notProcessedEthWei);
        }
    
        // finish ICO if cap reached
        if (totalTokensSold >= icoCap) {
          icoFinishTimestamp = now;
    
          finishIco();
        }
    
        // fire event
        InvestmentEvent(msg.sender, _actualInvestment, _purchasedTokensWei);
      }
    
      /**
       * @dev Calculate amount of tokens for received ETH
       * @param _totalTokensSoldBefore uint Amount of tokens sold before this investment [token wei]
       * @param _investedEthWei        uint Investment amount [ETH wei]
       * @return Purchased amount of tokens [token wei]
       */
      function calculatePurchasedTokens(
        uint _totalTokensSoldBefore,
        uint _investedEthWei)
        constant public returns (uint _purchasedTokensWei, uint _notProcessedEthWei)
      {
        _purchasedTokensWei = 0;
        _notProcessedEthWei = _investedEthWei;
    
        uint _landmarkPrice;
        uint _maxLandmarkTokensWei;
        uint _maxLandmarkEthWei;
        bool _isCapReached = false;
        do {
          // get landmark values
          _landmarkPrice = calculateLandmarkPrice(_totalTokensSoldBefore + _purchasedTokensWei);
          _maxLandmarkTokensWei = landmarkSize - ((_totalTokensSoldBefore + _purchasedTokensWei) % landmarkSize);
          if (_totalTokensSoldBefore + _purchasedTokensWei + _maxLandmarkTokensWei >= icoCap) {
            _maxLandmarkTokensWei = icoCap - _totalTokensSoldBefore - _purchasedTokensWei;
            _isCapReached = true;
          }
          _maxLandmarkEthWei = _maxLandmarkTokensWei * _landmarkPrice / (10 ** 18);
    
          // check investment against landmark values
          if (_notProcessedEthWei >= _maxLandmarkEthWei) {
            _purchasedTokensWei += _maxLandmarkTokensWei;
            _notProcessedEthWei -= _maxLandmarkEthWei;
          }
          else {
            _purchasedTokensWei += _notProcessedEthWei * (10 ** 18) / _landmarkPrice;
            _notProcessedEthWei = 0;
          }
        }
        while ((_notProcessedEthWei > 0) && (_isCapReached == false));
    
        assert(_purchasedTokensWei > 0);
    
        return (_purchasedTokensWei, _notProcessedEthWei);
      }
    
    
      /* Refunds */
    
      function angelBurn(
        address _investor,
        uint _returnedTokensWei
      )
        external returns (uint)
      {
        require(msg.sender == address(angelToken));
        require(now >= icoLaunchTimestamp && now < secondRefundRoundFinishTimestamp);
    
        uint _notProcessedTokensWei = _returnedTokensWei;
        uint _refundedEthWei = 0;
    
        uint _allRecordsNumber = investments[_investor].length;
        uint _recordMaxReturnedTokensWei = 0;
        uint _recordTokensWeiToProcess = 0;
        uint _tokensSoldWei = 0;
        uint _recordRefundedEthWei = 0;
        uint _recordNotProcessedTokensWei = 0;
        for (uint _recordID = 0; _recordID < _allRecordsNumber; _recordID += 1) {
          if (investments[_investor][_recordID].purchasedTokensWei <= investments[_investor][_recordID].returnedTokensWei ||
              investments[_investor][_recordID].investedEthWei <= investments[_investor][_recordID].refundedEthWei) {
            // tokens already refunded
            continue;
          }
    
          // calculate amount of tokens to refund with this record
          _recordMaxReturnedTokensWei = investments[_investor][_recordID].purchasedTokensWei -
                                        investments[_investor][_recordID].returnedTokensWei;
          _recordTokensWeiToProcess = (_notProcessedTokensWei < _recordMaxReturnedTokensWei) ? _notProcessedTokensWei :
                                                                                               _recordMaxReturnedTokensWei;
          assert(_recordTokensWeiToProcess > 0);
    
          // calculate amount of ETH to send back
          _tokensSoldWei = investments[_investor][_recordID].tokensSoldBeforeWei + investments[_investor][_recordID].returnedTokensWei;
          (_recordRefundedEthWei, _recordNotProcessedTokensWei) = calculateRefundedEth(_tokensSoldWei, _recordTokensWeiToProcess);
          if (_recordRefundedEthWei > (investments[_investor][_recordID].investedEthWei - investments[_investor][_recordID].refundedEthWei)) {
            // this can happen due to rounding error
            _recordRefundedEthWei = (investments[_investor][_recordID].investedEthWei - investments[_investor][_recordID].refundedEthWei);
          }
          assert(_recordRefundedEthWei > 0);
          assert(_recordNotProcessedTokensWei == 0);
    
          // persist changes to the storage
          _refundedEthWei += _recordRefundedEthWei;
          _notProcessedTokensWei -= _recordTokensWeiToProcess;
    
          investments[_investor][_recordID].refundedEthWei += _recordRefundedEthWei;
          investments[_investor][_recordID].returnedTokensWei += _recordTokensWeiToProcess;
          assert(investments[_investor][_recordID].refundedEthWei <= investments[_investor][_recordID].investedEthWei);
          assert(investments[_investor][_recordID].returnedTokensWei <= investments[_investor][_recordID].purchasedTokensWei);
    
          // stop if we already refunded all tokens
          if (_notProcessedTokensWei == 0) {
            break;
          }
        }
    
        // throw if we do not have tokens to refund
        require(_notProcessedTokensWei < _returnedTokensWei);
        require(_refundedEthWei > 0);
    
        // calculate refund discount
        uint _refundedEthWeiWithDiscount = calculateRefundedEthWithDiscount(_refundedEthWei);
    
        // transfer ETH and remaining tokens
        angelToken.burn(_returnedTokensWei - _notProcessedTokensWei);
        if (_notProcessedTokensWei > 0) {
          angelToken.transfer(_investor, _notProcessedTokensWei);
        }
        _investor.transfer(_refundedEthWeiWithDiscount);
    
        // fire event
        RefundEvent(_investor, _refundedEthWeiWithDiscount, _returnedTokensWei - _notProcessedTokensWei);
      }
    
      /**
       * @dev Calculate discounted amount of ETH for refunded tokens
       * @param _refundedEthWei uint Calculated amount of ETH to refund [ETH wei]
       * @return Discounted amount of ETH for refunded [ETH wei]
       */
      function calculateRefundedEthWithDiscount(
        uint _refundedEthWei
      )
        public constant returns (uint)
      {
        if (now <= firstRefundRoundFinishTimestamp) {
          return (_refundedEthWei * firstRefundRoundRateNumerator / firstRefundRoundRateDenominator);
        }
        else {
          return (_refundedEthWei * secondRefundRoundRateNumerator / secondRefundRoundRateDenominator);
        }
      }
    
      /**
       * @dev Calculate amount of ETH for refunded tokens. Just abstract price ladder
       * @param _totalTokensSoldBefore     uint Amount of tokens that have been sold (starting point) [token wei]
       * @param _returnedTokensWei uint Amount of tokens to refund [token wei]
       * @return Refunded amount of ETH [ETH wei] (without discounts)
       */
      function calculateRefundedEth(
        uint _totalTokensSoldBefore,
        uint _returnedTokensWei
      )
        public constant returns (uint _refundedEthWei, uint _notProcessedTokensWei)
      {
        _refundedEthWei = 0;
        uint _refundedTokensWei = 0;
        _notProcessedTokensWei = _returnedTokensWei;
    
        uint _landmarkPrice = 0;
        uint _maxLandmarkTokensWei = 0;
        uint _maxLandmarkEthWei = 0;
        bool _isCapReached = false;
        do {
          // get landmark values
          _landmarkPrice = calculateLandmarkPrice(_totalTokensSoldBefore + _refundedTokensWei);
          _maxLandmarkTokensWei = landmarkSize - ((_totalTokensSoldBefore + _refundedTokensWei) % landmarkSize);
          if (_totalTokensSoldBefore + _refundedTokensWei + _maxLandmarkTokensWei >= icoCap) {
            _maxLandmarkTokensWei = icoCap - _totalTokensSoldBefore - _refundedTokensWei;
            _isCapReached = true;
          }
          _maxLandmarkEthWei = _maxLandmarkTokensWei * _landmarkPrice / (10 ** 18);
    
          // check investment against landmark values
          if (_notProcessedTokensWei > _maxLandmarkTokensWei) {
            _refundedEthWei += _maxLandmarkEthWei;
            _refundedTokensWei += _maxLandmarkTokensWei;
            _notProcessedTokensWei -= _maxLandmarkTokensWei;
          }
          else {
            _refundedEthWei += _notProcessedTokensWei * _landmarkPrice / (10 ** 18);
            _refundedTokensWei += _notProcessedTokensWei;
            _notProcessedTokensWei = 0;
          }
        }
        while ((_notProcessedTokensWei > 0) && (_isCapReached == false));
    
        assert(_refundedEthWei > 0);
    
        return (_refundedEthWei, _notProcessedTokensWei);
      }
    
    
      /* Calculation of the price */
    
      /**
       * @dev Calculate price for tokens
       * @param _totalTokensSoldBefore uint Amount of tokens sold before [token wei]
       * @return Calculated price
       */
      function calculateLandmarkPrice(uint _totalTokensSoldBefore) public constant returns (uint) {
        return initialTokenPrice + initialTokenPrice
                                   * landmarkPriceStepNumerator / landmarkPriceStepDenominator
                                   * (_totalTokensSoldBefore / landmarkSize);
      }
    
    
      /* Lifecycle */
    
      function finishIco() public {
        require(now >= icoFinishTimestamp);
        require(isIcoFinished == false);
    
        isIcoFinished = true;
    
        angelToken.unpauseContract();
      }
    
      function withdrawFoundationFunds() external {
        require(now > firstRefundRoundFinishTimestamp);
    
        if (now > firstRefundRoundFinishTimestamp && now <= secondRefundRoundFinishTimestamp) {
          require(firstRefundRoundFundsWithdrawal == false);
    
          firstRefundRoundFundsWithdrawal = true;
          angelFoundationAddress.transfer(this.balance * afterFirstRefundRoundFundsReleaseNumerator / afterFirstRefundRoundFundsReleaseDenominator);
        } else {
          angelFoundationAddress.transfer(this.balance);
        }
      }
    }

    File 2 of 2: AngelToken
    pragma solidity ^0.4.16;
    
    
    /**
     * @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;
    
    
      /**
       * @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) external onlyOwner {
        if (newOwner != address(0)) {
          owner = newOwner;
        }
      }
    
    }
    
    
    
    /**
     * @title SafeMath
     * @dev Math operations with safety checks that throw on error
     */
    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) {
        // 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 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;
      }
    }
    
    
    
    /**
     * @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 constant returns (uint256);
      function transfer(address to, uint256 value) public returns (bool);
      event Transfer(address indexed from, address indexed to, uint256 value);
    }
    
    
    
    /**
     * @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) {
        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 constant returns (uint256 balance) {
        return balances[_owner];
      }
    
    }
    
    
    
    /**
     * @title ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    contract ERC20 is ERC20Basic {
      function allowance(address owner, address spender) public constant 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);
    }
    
    
    
    /**
     * @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)) 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 amout of tokens to be transfered
       */
      function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        var _allowance = allowed[_from][msg.sender];
    
        // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
        // require (_value <= _allowance);
    
        balances[_to] = balances[_to].add(_value);
        balances[_from] = balances[_from].sub(_value);
        allowed[_from][msg.sender] = _allowance.sub(_value);
        Transfer(_from, _to, _value);
        return true;
      }
    
      /**
       * @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender.
       * @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) {
    
        // To change the approve amount you first have to reduce the addresses`
        //  allowance to zero by calling `approve(_spender, 0)` if it is not
        //  already 0 to mitigate the race condition described here:
        //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        require((_value == 0) || (allowed[msg.sender][_spender] == 0));
    
        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 specifing the amount of tokens still avaible for the spender.
       */
      function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
        return allowed[_owner][_spender];
      }
    
    }
    
    
    
    /**
     * @title Manageable
     * @dev Contract that allows to grant permissions to any address
     * @dev In real life we are no able to perform all actions with just one Ethereum address
     * @dev because risks are too high.
     * @dev Instead owner delegates rights to manage an contract to the different addresses and
     * @dev stay able to revoke permissions at any time.
     */
    contract Manageable is Ownable {
    
      /* Storage */
    
      mapping (address => bool) managerEnabled;  // hard switch for a manager - on/off
      mapping (address => mapping (string => bool)) managerPermissions;  // detailed info about manager`s permissions
    
    
      /* Events */
    
      event ManagerEnabledEvent(address indexed manager);
      event ManagerDisabledEvent(address indexed manager);
      event ManagerPermissionGrantedEvent(address indexed manager, string permission);
      event ManagerPermissionRevokedEvent(address indexed manager, string permission);
    
    
      /* Configure contract */
    
      /**
       * @dev Function to add new manager
       * @param _manager address New manager
       */
      function enableManager(address _manager) external onlyOwner onlyValidAddress(_manager) {
        require(managerEnabled[_manager] == false);
    
        managerEnabled[_manager] = true;
        ManagerEnabledEvent(_manager);
      }
    
      /**
       * @dev Function to remove existing manager
       * @param _manager address Existing manager
       */
      function disableManager(address _manager) external onlyOwner onlyValidAddress(_manager) {
        require(managerEnabled[_manager] == true);
    
        managerEnabled[_manager] = false;
        ManagerDisabledEvent(_manager);
      }
    
      /**
       * @dev Function to grant new permission to the manager
       * @param _manager        address Existing manager
       * @param _permissionName string  Granted permission name
       */
      function grantManagerPermission(
        address _manager, string _permissionName
      )
        external
        onlyOwner
        onlyValidAddress(_manager)
        onlyValidPermissionName(_permissionName)
      {
        require(managerPermissions[_manager][_permissionName] == false);
    
        managerPermissions[_manager][_permissionName] = true;
        ManagerPermissionGrantedEvent(_manager, _permissionName);
      }
    
      /**
       * @dev Function to revoke permission of the manager
       * @param _manager        address Existing manager
       * @param _permissionName string  Revoked permission name
       */
      function revokeManagerPermission(
        address _manager, string _permissionName
      )
        external
        onlyOwner
        onlyValidAddress(_manager)
        onlyValidPermissionName(_permissionName)
      {
        require(managerPermissions[_manager][_permissionName] == true);
    
        managerPermissions[_manager][_permissionName] = false;
        ManagerPermissionRevokedEvent(_manager, _permissionName);
      }
    
    
      /* Getters */
    
      /**
       * @dev Function to check manager status
       * @param _manager address Manager`s address
       * @return True if manager is enabled
       */
      function isManagerEnabled(address _manager) public constant onlyValidAddress(_manager) returns (bool) {
        return managerEnabled[_manager];
      }
    
      /**
       * @dev Function to check permissions of a manager
       * @param _manager        address Manager`s address
       * @param _permissionName string  Permission name
       * @return True if manager has been granted needed permission
       */
      function isPermissionGranted(
        address _manager, string _permissionName
      )
        public
        constant
        onlyValidAddress(_manager)
        onlyValidPermissionName(_permissionName)
        returns (bool)
      {
        return managerPermissions[_manager][_permissionName];
      }
    
      /**
       * @dev Function to check if the manager can perform the action or not
       * @param _manager        address Manager`s address
       * @param _permissionName string  Permission name
       * @return True if manager is enabled and has been granted needed permission
       */
      function isManagerAllowed(
        address _manager, string _permissionName
      )
        public
        constant
        onlyValidAddress(_manager)
        onlyValidPermissionName(_permissionName)
        returns (bool)
      {
        return (managerEnabled[_manager] && managerPermissions[_manager][_permissionName]);
      }
    
    
      /* Helpers */
    
      /**
       * @dev Modifier to check manager address
       */
      modifier onlyValidAddress(address _manager) {
        require(_manager != address(0x0));
        _;
      }
    
      /**
       * @dev Modifier to check name of manager permission
       */
      modifier onlyValidPermissionName(string _permissionName) {
        require(bytes(_permissionName).length != 0);
        _;
      }
    
    
      /* Outcome */
    
      /**
       * @dev Modifier to use in derived contracts
       */
      modifier onlyAllowedManager(string _permissionName) {
        require(isManagerAllowed(msg.sender, _permissionName) == true);
        _;
      }
    }
    
    
    
    /**
     * @title Pausable
     * @dev Base contract which allows children to implement an emergency stop mechanism.
     * @dev Based on zeppelin's Pausable, but integrated with Manageable
     * @dev Contract is in paused state by default and should be explicitly unlocked
     */
    contract Pausable is Manageable {
    
      /**
       * Events
       */
    
      event PauseEvent();
      event UnpauseEvent();
    
    
      /**
       * Storage
       */
    
      bool paused = true;
    
    
      /**
       * @dev modifier to allow actions only when the contract IS paused
       */
      modifier whenContractNotPaused() {
        require(paused == false);
        _;
      }
    
      /**
       * @dev modifier to allow actions only when the contract IS NOT paused
       */
      modifier whenContractPaused {
        require(paused == true);
        _;
      }
    
      /**
       * @dev called by the manager to pause, triggers stopped state
       */
      function pauseContract() external onlyAllowedManager('pause_contract') whenContractNotPaused {
        paused = true;
        PauseEvent();
      }
    
      /**
       * @dev called by the manager to unpause, returns to normal state
       */
      function unpauseContract() external onlyAllowedManager('unpause_contract') whenContractPaused {
        paused = false;
        UnpauseEvent();
      }
    
      /**
       * @dev The getter for "paused" contract variable
       */
      function getPaused() external constant returns (bool) {
        return paused;
      }
    }
    
    
    
    /**
     * @title NamedToken
     */
    contract NamedToken {
      string public name;
      string public symbol;
      uint8 public decimals;
    
      function NamedToken(string _name, string _symbol, uint8 _decimals) public {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
      }
    
      /**
       * @dev Function to calculate hash of the token`s name.
       * @dev Function needed because we can not just return name of the token to another contract - strings have variable length
       * @return Hash of the token`s name
       */
      function getNameHash() external constant returns (bytes32 result){
        return keccak256(name);
      }
    
      /**
       * @dev Function to calculate hash of the token`s symbol.
       * @dev Function needed because we can not just return symbol of the token to another contract - strings have variable length
       * @return Hash of the token`s symbol
       */
      function getSymbolHash() external constant returns (bytes32 result){
        return keccak256(symbol);
      }
    }
    
    
    
    /**
     * @title AngelToken
     */
    contract AngelToken is StandardToken, NamedToken, Pausable {
    
      /* Events */
    
      event MintEvent(address indexed account, uint value);
      event BurnEvent(address indexed account, uint value);
      event SpendingBlockedEvent(address indexed account);
      event SpendingUnblockedEvent(address indexed account);
    
    
      /* Storage */
    
      address public centralBankAddress = 0x0;
      mapping (address => uint) spendingBlocksNumber;
    
    
      /* Constructor */
    
      function AngelToken() public NamedToken('Angel Token', 'ANGL', 18) {
        centralBankAddress = msg.sender;
      }
    
    
      /* Methods */
    
      function transfer(address _to, uint _value) public returns (bool) {
        if (_to != centralBankAddress) {
          require(!paused);
        }
        require(spendingBlocksNumber[msg.sender] == 0);
    
        bool result = super.transfer(_to, _value);
        if (result == true && _to == centralBankAddress) {
          AngelCentralBank(centralBankAddress).angelBurn(msg.sender, _value);
        }
        return result;
      }
    
      function approve(address _spender, uint _value) public whenContractNotPaused returns (bool){
        return super.approve(_spender, _value);
      }
    
      function transferFrom(address _from, address _to, uint _value) public whenContractNotPaused returns (bool){
        require(spendingBlocksNumber[_from] == 0);
    
        bool result = super.transferFrom(_from, _to, _value);
        if (result == true && _to == centralBankAddress) {
          AngelCentralBank(centralBankAddress).angelBurn(_from, _value);
        }
        return result;
      }
    
    
      function mint(address _account, uint _value) external onlyAllowedManager('mint_tokens') {
        balances[_account] = balances[_account].add(_value);
        totalSupply = totalSupply.add(_value);
        MintEvent(_account, _value);
        Transfer(address(0x0), _account, _value); // required for blockexplorers
      }
    
      function burn(uint _value) external onlyAllowedManager('burn_tokens') {
        balances[msg.sender] = balances[msg.sender].sub(_value);
        totalSupply = totalSupply.sub(_value);
        BurnEvent(msg.sender, _value);
      }
    
      function blockSpending(address _account) external onlyAllowedManager('block_spending') {
        spendingBlocksNumber[_account] = spendingBlocksNumber[_account].add(1);
        SpendingBlockedEvent(_account);
      }
    
      function unblockSpending(address _account) external onlyAllowedManager('unblock_spending') {
        spendingBlocksNumber[_account] = spendingBlocksNumber[_account].sub(1);
        SpendingUnblockedEvent(_account);
      }
    }
    
    
    
    /**
     * @title AngelCentralBank
     *
     * @dev Crowdsale and escrow contract
     */
    contract AngelCentralBank {
    
      /* Data structures */
    
      struct InvestmentRecord {
        uint tokensSoldBeforeWei;
        uint investedEthWei;
        uint purchasedTokensWei;
        uint refundedEthWei;
        uint returnedTokensWei;
      }
    
    
      /* Storage - config */
    
      uint public constant icoCap = 70000000 * (10 ** 18);
    
      uint public initialTokenPrice = 1 * (10 ** 18) / (10 ** 4); // means 0.0001 ETH for one token
    
      uint public constant landmarkSize = 1000000 * (10 ** 18);
      uint public constant landmarkPriceStepNumerator = 10;
      uint public constant landmarkPriceStepDenominator = 100;
    
      uint public constant firstRefundRoundRateNumerator = 80;
      uint public constant firstRefundRoundRateDenominator = 100;
      uint public constant secondRefundRoundRateNumerator = 40;
      uint public constant secondRefundRoundRateDenominator = 100;
    
      uint public constant initialFundsReleaseNumerator = 20; // part of investment
      uint public constant initialFundsReleaseDenominator = 100;
      uint public constant afterFirstRefundRoundFundsReleaseNumerator = 50; // part of remaining funds
      uint public constant afterFirstRefundRoundFundsReleaseDenominator = 100;
    
      uint public constant angelFoundationShareNumerator = 30;
      uint public constant angelFoundationShareDenominator = 100;
    
      /* Storage - state */
    
      address public angelFoundationAddress = address(0x2b0556a6298eA3D35E90F1df32cc126b31F59770);
      uint public icoLaunchTimestamp = 1511784000;  // November 27th 12:00 GMT
      uint public icoFinishTimestamp = 1513727999;  // December 19th 23:59:59 GMT
      uint public firstRefundRoundFinishTimestamp = 1520424000;  // March 7th 2018 12:00 GMT
      uint public secondRefundRoundFinishTimestamp = 1524744000;  // April 26th 2018 12:00 GMT
    
    
      AngelToken public angelToken;
    
      mapping (address => InvestmentRecord[]) public investments; // investorAddress => list of investments
      mapping (address => bool) public investors;
      uint public totalInvestors = 0;
      uint public totalTokensSold = 0;
    
      bool isIcoFinished = false;
      bool firstRefundRoundFundsWithdrawal = false;
    
    
      /* Events */
    
      event InvestmentEvent(address indexed investor, uint eth, uint angel);
      event RefundEvent(address indexed investor, uint eth, uint angel);
    
    
      /* Constructor and config */
    
      function AngelCentralBank() public {
        angelToken = new AngelToken();
        angelToken.enableManager(address(this));
        angelToken.grantManagerPermission(address(this), 'mint_tokens');
        angelToken.grantManagerPermission(address(this), 'burn_tokens');
        angelToken.grantManagerPermission(address(this), 'unpause_contract');
        angelToken.transferOwnership(angelFoundationAddress);
      }
    
      /* Investments */
    
      /**
       * @dev Fallback function receives ETH and sends tokens back
       */
      function () public payable {
        angelRaise();
      }
    
      /**
       * @dev Process new ETH investment and sends tokens back
       */
      function angelRaise() internal {
        require(msg.value > 0);
        require(now >= icoLaunchTimestamp && now < icoFinishTimestamp);
    
        // calculate amount of tokens for received ETH
        uint _purchasedTokensWei = 0;
        uint _notProcessedEthWei = 0;
        (_purchasedTokensWei, _notProcessedEthWei) = calculatePurchasedTokens(totalTokensSold, msg.value);
        uint _actualInvestment = (msg.value - _notProcessedEthWei);
    
        // create record for the investment
        uint _newRecordIndex = investments[msg.sender].length;
        investments[msg.sender].length += 1;
        investments[msg.sender][_newRecordIndex].tokensSoldBeforeWei = totalTokensSold;
        investments[msg.sender][_newRecordIndex].investedEthWei = _actualInvestment;
        investments[msg.sender][_newRecordIndex].purchasedTokensWei = _purchasedTokensWei;
        investments[msg.sender][_newRecordIndex].refundedEthWei = 0;
        investments[msg.sender][_newRecordIndex].returnedTokensWei = 0;
    
        // calculate stats
        if (investors[msg.sender] == false) {
          totalInvestors += 1;
        }
        investors[msg.sender] = true;
        totalTokensSold += _purchasedTokensWei;
    
        // transfer tokens and ETH
        angelToken.mint(msg.sender, _purchasedTokensWei);
        angelToken.mint(angelFoundationAddress,
                        _purchasedTokensWei * angelFoundationShareNumerator / (angelFoundationShareDenominator - angelFoundationShareNumerator));
        angelFoundationAddress.transfer(_actualInvestment * initialFundsReleaseNumerator / initialFundsReleaseDenominator);
        if (_notProcessedEthWei > 0) {
          msg.sender.transfer(_notProcessedEthWei);
        }
    
        // finish ICO if cap reached
        if (totalTokensSold >= icoCap) {
          icoFinishTimestamp = now;
    
          finishIco();
        }
    
        // fire event
        InvestmentEvent(msg.sender, _actualInvestment, _purchasedTokensWei);
      }
    
      /**
       * @dev Calculate amount of tokens for received ETH
       * @param _totalTokensSoldBefore uint Amount of tokens sold before this investment [token wei]
       * @param _investedEthWei        uint Investment amount [ETH wei]
       * @return Purchased amount of tokens [token wei]
       */
      function calculatePurchasedTokens(
        uint _totalTokensSoldBefore,
        uint _investedEthWei)
        constant public returns (uint _purchasedTokensWei, uint _notProcessedEthWei)
      {
        _purchasedTokensWei = 0;
        _notProcessedEthWei = _investedEthWei;
    
        uint _landmarkPrice;
        uint _maxLandmarkTokensWei;
        uint _maxLandmarkEthWei;
        bool _isCapReached = false;
        do {
          // get landmark values
          _landmarkPrice = calculateLandmarkPrice(_totalTokensSoldBefore + _purchasedTokensWei);
          _maxLandmarkTokensWei = landmarkSize - ((_totalTokensSoldBefore + _purchasedTokensWei) % landmarkSize);
          if (_totalTokensSoldBefore + _purchasedTokensWei + _maxLandmarkTokensWei >= icoCap) {
            _maxLandmarkTokensWei = icoCap - _totalTokensSoldBefore - _purchasedTokensWei;
            _isCapReached = true;
          }
          _maxLandmarkEthWei = _maxLandmarkTokensWei * _landmarkPrice / (10 ** 18);
    
          // check investment against landmark values
          if (_notProcessedEthWei >= _maxLandmarkEthWei) {
            _purchasedTokensWei += _maxLandmarkTokensWei;
            _notProcessedEthWei -= _maxLandmarkEthWei;
          }
          else {
            _purchasedTokensWei += _notProcessedEthWei * (10 ** 18) / _landmarkPrice;
            _notProcessedEthWei = 0;
          }
        }
        while ((_notProcessedEthWei > 0) && (_isCapReached == false));
    
        assert(_purchasedTokensWei > 0);
    
        return (_purchasedTokensWei, _notProcessedEthWei);
      }
    
    
      /* Refunds */
    
      function angelBurn(
        address _investor,
        uint _returnedTokensWei
      )
        external returns (uint)
      {
        require(msg.sender == address(angelToken));
        require(now >= icoLaunchTimestamp && now < secondRefundRoundFinishTimestamp);
    
        uint _notProcessedTokensWei = _returnedTokensWei;
        uint _refundedEthWei = 0;
    
        uint _allRecordsNumber = investments[_investor].length;
        uint _recordMaxReturnedTokensWei = 0;
        uint _recordTokensWeiToProcess = 0;
        uint _tokensSoldWei = 0;
        uint _recordRefundedEthWei = 0;
        uint _recordNotProcessedTokensWei = 0;
        for (uint _recordID = 0; _recordID < _allRecordsNumber; _recordID += 1) {
          if (investments[_investor][_recordID].purchasedTokensWei <= investments[_investor][_recordID].returnedTokensWei ||
              investments[_investor][_recordID].investedEthWei <= investments[_investor][_recordID].refundedEthWei) {
            // tokens already refunded
            continue;
          }
    
          // calculate amount of tokens to refund with this record
          _recordMaxReturnedTokensWei = investments[_investor][_recordID].purchasedTokensWei -
                                        investments[_investor][_recordID].returnedTokensWei;
          _recordTokensWeiToProcess = (_notProcessedTokensWei < _recordMaxReturnedTokensWei) ? _notProcessedTokensWei :
                                                                                               _recordMaxReturnedTokensWei;
          assert(_recordTokensWeiToProcess > 0);
    
          // calculate amount of ETH to send back
          _tokensSoldWei = investments[_investor][_recordID].tokensSoldBeforeWei + investments[_investor][_recordID].returnedTokensWei;
          (_recordRefundedEthWei, _recordNotProcessedTokensWei) = calculateRefundedEth(_tokensSoldWei, _recordTokensWeiToProcess);
          if (_recordRefundedEthWei > (investments[_investor][_recordID].investedEthWei - investments[_investor][_recordID].refundedEthWei)) {
            // this can happen due to rounding error
            _recordRefundedEthWei = (investments[_investor][_recordID].investedEthWei - investments[_investor][_recordID].refundedEthWei);
          }
          assert(_recordRefundedEthWei > 0);
          assert(_recordNotProcessedTokensWei == 0);
    
          // persist changes to the storage
          _refundedEthWei += _recordRefundedEthWei;
          _notProcessedTokensWei -= _recordTokensWeiToProcess;
    
          investments[_investor][_recordID].refundedEthWei += _recordRefundedEthWei;
          investments[_investor][_recordID].returnedTokensWei += _recordTokensWeiToProcess;
          assert(investments[_investor][_recordID].refundedEthWei <= investments[_investor][_recordID].investedEthWei);
          assert(investments[_investor][_recordID].returnedTokensWei <= investments[_investor][_recordID].purchasedTokensWei);
    
          // stop if we already refunded all tokens
          if (_notProcessedTokensWei == 0) {
            break;
          }
        }
    
        // throw if we do not have tokens to refund
        require(_notProcessedTokensWei < _returnedTokensWei);
        require(_refundedEthWei > 0);
    
        // calculate refund discount
        uint _refundedEthWeiWithDiscount = calculateRefundedEthWithDiscount(_refundedEthWei);
    
        // transfer ETH and remaining tokens
        angelToken.burn(_returnedTokensWei - _notProcessedTokensWei);
        if (_notProcessedTokensWei > 0) {
          angelToken.transfer(_investor, _notProcessedTokensWei);
        }
        _investor.transfer(_refundedEthWeiWithDiscount);
    
        // fire event
        RefundEvent(_investor, _refundedEthWeiWithDiscount, _returnedTokensWei - _notProcessedTokensWei);
      }
    
      /**
       * @dev Calculate discounted amount of ETH for refunded tokens
       * @param _refundedEthWei uint Calculated amount of ETH to refund [ETH wei]
       * @return Discounted amount of ETH for refunded [ETH wei]
       */
      function calculateRefundedEthWithDiscount(
        uint _refundedEthWei
      )
        public constant returns (uint)
      {
        if (now <= firstRefundRoundFinishTimestamp) {
          return (_refundedEthWei * firstRefundRoundRateNumerator / firstRefundRoundRateDenominator);
        }
        else {
          return (_refundedEthWei * secondRefundRoundRateNumerator / secondRefundRoundRateDenominator);
        }
      }
    
      /**
       * @dev Calculate amount of ETH for refunded tokens. Just abstract price ladder
       * @param _totalTokensSoldBefore     uint Amount of tokens that have been sold (starting point) [token wei]
       * @param _returnedTokensWei uint Amount of tokens to refund [token wei]
       * @return Refunded amount of ETH [ETH wei] (without discounts)
       */
      function calculateRefundedEth(
        uint _totalTokensSoldBefore,
        uint _returnedTokensWei
      )
        public constant returns (uint _refundedEthWei, uint _notProcessedTokensWei)
      {
        _refundedEthWei = 0;
        uint _refundedTokensWei = 0;
        _notProcessedTokensWei = _returnedTokensWei;
    
        uint _landmarkPrice = 0;
        uint _maxLandmarkTokensWei = 0;
        uint _maxLandmarkEthWei = 0;
        bool _isCapReached = false;
        do {
          // get landmark values
          _landmarkPrice = calculateLandmarkPrice(_totalTokensSoldBefore + _refundedTokensWei);
          _maxLandmarkTokensWei = landmarkSize - ((_totalTokensSoldBefore + _refundedTokensWei) % landmarkSize);
          if (_totalTokensSoldBefore + _refundedTokensWei + _maxLandmarkTokensWei >= icoCap) {
            _maxLandmarkTokensWei = icoCap - _totalTokensSoldBefore - _refundedTokensWei;
            _isCapReached = true;
          }
          _maxLandmarkEthWei = _maxLandmarkTokensWei * _landmarkPrice / (10 ** 18);
    
          // check investment against landmark values
          if (_notProcessedTokensWei > _maxLandmarkTokensWei) {
            _refundedEthWei += _maxLandmarkEthWei;
            _refundedTokensWei += _maxLandmarkTokensWei;
            _notProcessedTokensWei -= _maxLandmarkTokensWei;
          }
          else {
            _refundedEthWei += _notProcessedTokensWei * _landmarkPrice / (10 ** 18);
            _refundedTokensWei += _notProcessedTokensWei;
            _notProcessedTokensWei = 0;
          }
        }
        while ((_notProcessedTokensWei > 0) && (_isCapReached == false));
    
        assert(_refundedEthWei > 0);
    
        return (_refundedEthWei, _notProcessedTokensWei);
      }
    
    
      /* Calculation of the price */
    
      /**
       * @dev Calculate price for tokens
       * @param _totalTokensSoldBefore uint Amount of tokens sold before [token wei]
       * @return Calculated price
       */
      function calculateLandmarkPrice(uint _totalTokensSoldBefore) public constant returns (uint) {
        return initialTokenPrice + initialTokenPrice
                                   * landmarkPriceStepNumerator / landmarkPriceStepDenominator
                                   * (_totalTokensSoldBefore / landmarkSize);
      }
    
    
      /* Lifecycle */
    
      function finishIco() public {
        require(now >= icoFinishTimestamp);
        require(isIcoFinished == false);
    
        isIcoFinished = true;
    
        angelToken.unpauseContract();
      }
    
      function withdrawFoundationFunds() external {
        require(now > firstRefundRoundFinishTimestamp);
    
        if (now > firstRefundRoundFinishTimestamp && now <= secondRefundRoundFinishTimestamp) {
          require(firstRefundRoundFundsWithdrawal == false);
    
          firstRefundRoundFundsWithdrawal = true;
          angelFoundationAddress.transfer(this.balance * afterFirstRefundRoundFundsReleaseNumerator / afterFirstRefundRoundFundsReleaseDenominator);
        } else {
          angelFoundationAddress.transfer(this.balance);
        }
      }
    }