pragma solidity ^0.4.24;

/**
 * 
 * ██████╗ ███████╗████████╗███████╗██████╗ ███╗   ██╗ █████╗ ██╗     
 * ██╔══██╗██╔════╝╚══██╔══╝██╔════╝██╔══██╗████╗  ██║██╔══██╗██║     
 * ██████╔╝█████╗     ██║   █████╗  ██████╔╝██╔██╗ ██║███████║██║   
 * ██╔══██╗██╔══╝     ██║   ██╔══╝  ██╔══██╗██║╚██╗██║██╔══██║██║     
 * ██║  ██║███████╗   ██║   ███████╗██║  ██║██║ ╚████║██║  ██║███████╗
 * ╚═╝  ╚═╝╚══════╝   ╚═╝   ╚══════╝╚═╝  ╚═╝╚═╝  ╚═══╝╚═╝  ╚═╝╚══════╝
 * 
 *  Contacts:
 * 
 *   -- t.me/Reternal
 *   -- https://www.reternal.net
 * 
 * - GAIN PER 24 HOURS:
 * 
 *     -- Individual balance < 1 Ether: 3.55%
 *     -- Individual balance >= 1 Ether: 3.65%
 *     -- Individual balance >= 4 Ether: 3.75%
 *     -- Individual balance >= 12 Ether: 3.85%
 *     -- Individual balance >= 50 Ether: 4%
 * 
 *     -- Contract balance < 200 Ether: 0%
 *     -- Contract balance >= 200 Ether: 0.30%
 *     -- Contract balance >= 500 Ether: 0.40%
 *     -- Contract balance >= 900 Ether: 0.50%
 *     -- Contract balance >= 1500 Ether: 0.65%
 *     -- Contract balance >= 2000 Ether: 0.80%
 * 
 *  - Minimal contribution 0.01 eth
 *  - Contribution allocation schemes:
 *    -- 95% payments
 *    -- 5% Marketing + Operating Expenses
 * 
 * - How to use:
 *  1. Send from your personal ETH wallet to the smart-contract address any amount more than or equal to 0.01 ETH
 *  2. Add your refferer's wallet to a HEX data in your transaction to 
 *     get a bonus amount back to your wallet only for the FIRST deposit
 *     IMPORTANT: if you want to support Reternal project, you can leave your HEX data field empty, 
 *                if you have no referrer and do not want to support Reternal, you can type 'noreferrer'
 *                if there is no referrer, you will not get any bonuses
 *  3. Use etherscan.io to verify your transaction 
 *  4. Claim your dividents by sending 0 ether transaction (available anytime)
 *  5. You can reinvest anytime you want
 *
 * RECOMMENDED GAS LIMIT: 200000
 * RECOMMENDED GAS PRICE: https://ethgasstation.info/
 * 
 * The smart-contract has a "restart" function, more info at www.reternal.net
 * 
 * If you want to check your dividents, you can use etherscan.io site, following the "Internal Txns" tab of your wallet
 * WARNING: do not use exchanges' wallets - you will loose your funds. Only use your personal wallet for transactions 
 * 
 */

contract Reternal {
    
    // Investor's data storage
    mapping (address => Investor) public investors;
    address[] public addresses;
    
    struct Investor
    {
        uint id;
        uint deposit;
        uint depositCount;
        uint block;
        address referrer;
    }
    
    uint constant public MINIMUM_INVEST = 10000000000000000 wei;
    address defaultReferrer = 0x25EDFd665C2898c2898E499Abd8428BaC616a0ED;
    
    uint public round;
    uint public totalDepositAmount;
    bool public pause;
    uint public restartBlock;
    bool ref_flag;
    
    // Investors' dividents increase goals due to a bank growth
    uint bank1 = 200e18; // 200 eth
    uint bank2 = 500e18; // 500 eth
    uint bank3 = 900e18; // 900 eth
    uint bank4 = 1500e18; // 1500 eth
    uint bank5 = 2000e18; // 2000 eth
    // Investors' dividents increase due to individual deposit amount
    uint dep1 = 1e18; // 1 ETH
    uint dep2 = 4e18; // 4 ETH
    uint dep3 = 12e18; // 12 ETH
    uint dep4 = 5e19; // 50 ETH
    
    event NewInvestor(address indexed investor, uint deposit, address referrer);
    event PayOffDividends(address indexed investor, uint value);
    event refPayout(address indexed investor, uint value, address referrer);
    event NewDeposit(address indexed investor, uint value);
    event NextRoundStarted(uint round, uint block, address addr, uint value);
    
    constructor() public {
        addresses.length = 1;
        round = 1;
        pause = false;
    }

    function restart() private {
        address addr;

        for (uint i = addresses.length - 1; i > 0; i--) {
            addr = addresses[i];
            addresses.length -= 1;
            delete investors[addr];
        }
        
        emit NextRoundStarted(round, block.number, msg.sender, msg.value);
        pause = false;
        round += 1;
        totalDepositAmount = 0;
        
        createDeposit();
    }

    function getRaisedPercents(address addr) internal view  returns(uint){
        // Individual deposit percentage sums up with 'Reternal total fund' percentage
        uint percent = getIndividualPercent() + getBankPercent();
        uint256 amount = investors[addr].deposit * percent / 100*(block.number-investors[addr].block)/6000;
        return(amount / 100);
    }
    
    function payDividends() private{
        require(investors[msg.sender].id > 0, "Investor not found.");
        // Investor's total raised amount
        uint amount = getRaisedPercents(msg.sender);
            
        if (address(this).balance < amount) {
            pause = true;
            restartBlock = block.number + 6000;
            return;
        }
        
        // Service fee deduction 
        uint FeeToWithdraw = amount * 5 / 100;
        uint payment = amount - FeeToWithdraw;
        
        address(0xD9bE11E7412584368546b1CaE64b6C384AE85ebB).transfer(FeeToWithdraw);
        msg.sender.transfer(payment);
        emit PayOffDividends(msg.sender, amount);
        
    }
    
    function createDeposit() private{
        Investor storage user = investors[msg.sender];
        
        if (user.id == 0) {
            
            // Check for malicious smart-contract
            msg.sender.transfer(0 wei);
            user.id = addresses.push(msg.sender);

            if (msg.data.length != 0) {
                address referrer = bytesToAddress(msg.data);
                
                // Check for referrer's registration. Check for self referring
                if (investors[referrer].id > 0 && referrer != msg.sender) {
                    user.referrer = referrer;
                    
                    // Cashback only for the first deposit
                    if (user.depositCount == 0) {
                        uint cashback = msg.value / 100;
                        if (msg.sender.send(cashback)) {
                            emit refPayout(msg.sender, cashback, referrer);
                        }
                    }
                }
            } else {
                // If data is empty:
                user.referrer = defaultReferrer;
            }
            
            emit NewInvestor(msg.sender, msg.value, referrer);
            
        } else {
            // Dividents payment for an investor
            payDividends();
        }
        
        // 2% from a referral deposit transfer to a referrer 
        uint payReferrer = msg.value * 2 / 100; 
        
        if (user.referrer == defaultReferrer) {
            user.referrer.transfer(payReferrer);
        } else {
            investors[referrer].deposit += payReferrer;
        }
        
        
        user.depositCount++;
        user.deposit += msg.value;
        user.block = block.number;
        totalDepositAmount += msg.value;
        emit NewDeposit(msg.sender, msg.value);
    }

    function() external payable {
        if(pause) {
            if (restartBlock <= block.number) { restart(); }
            require(!pause, "Eternal is restarting, wait for the block in restartBlock");
        } else {
            if (msg.value == 0) {
                payDividends();
                return;
            }
            require(msg.value >= MINIMUM_INVEST, "Too small amount, minimum 0.01 ether");
            createDeposit();
        }
    }
    
    function getBankPercent() public view returns(uint){
        
        uint contractBalance = address(this).balance;
        
        uint totalBank1 = bank1;
        uint totalBank2 = bank2;
        uint totalBank3 = bank3;
        uint totalBank4 = bank4;
        uint totalBank5 = bank5;
        
        if(contractBalance < totalBank1){
            return(0);
        }
        if(contractBalance >= totalBank1 && contractBalance < totalBank2){
            return(30);
        }
        if(contractBalance >= totalBank2 && contractBalance < totalBank3){
            return(40);
        }
        if(contractBalance >= totalBank3 && contractBalance < totalBank4){
            return(50);
        }
        if(contractBalance >= totalBank4 && contractBalance < totalBank5){
            return(65);
        }
        if(contractBalance >= totalBank5){
            return(80);
        }
    }

    function getIndividualPercent() public view returns(uint){
        
        uint userBalance = investors[msg.sender].deposit;
        
        uint totalDeposit1 = dep1;
        uint totalDeposit2 = dep2;
        uint totalDeposit3 = dep3;
        uint totalDeposit4 = dep4;
        
        if(userBalance < totalDeposit1){
            return(355);
        }
        if(userBalance >= totalDeposit1 && userBalance < totalDeposit2){
            return(365);
        }
        if(userBalance >= totalDeposit2 && userBalance < totalDeposit3){
            return(375);
        }
        if(userBalance >= totalDeposit3 && userBalance < totalDeposit4){
            return(385); 
        }
        if(userBalance >= totalDeposit4){
            return(400);
        }
    }
    
    function getInvestorCount() public view returns (uint) {
        return addresses.length - 1;
    }
    
    function bytesToAddress(bytes bys) private pure returns (address addr) {
        assembly {
            addr := mload(add(bys, 20))
        }
    }

}