contract ETR is ERC20("ETH Roll", "ETR"), Ownable {

    uint256 public tokensPerEth = 200000 * 1e8; //this is base tokens per entire ETH, since these tokens have 8 decimals vs. 18 for ETH 
    uint256 public startTime;
    uint256 public totalEthReceived;
    uint256 public constant referrerPercent = 25;
    uint256 public constant referreePercent = 10;
    uint256 public constant hardCap = 19444 ether;
    uint256 public constant softCap = 100 ether;
    bool public saleOpen;
    bool public softCapMet;

    event Purchased(address user, uint256 amountTokens);
    event Referred(address referrer, uint256 amountTokens);

    constructor(){
        _mint(owner, (7777777777 * 1e8)); 
        startTime = 1617062400; // Starts 30th march 2021 midnight
        saleOpen = true;
    }
    
    modifier onlyWhenSaleOpen() {
        require(block.timestamp > startTime, "Sale not started yet");
        _;
    }

    function buyTokens(address referralAddress) public payable onlyWhenSaleOpen() {
        require(saleOpen, "buyTokens: sale closed");
        require(referralAddress != msg.sender, "buyTokens: cannot refer self");
        if(!softCapMet && totalEthReceived + msg.value >= softCap) {
            softCapMet = true;
        }
        require(totalEthReceived + msg.value <= hardCap, "buyTokens: purchase would exceed hard cap");
        totalEthReceived += msg.value;
        uint256 basePurchase = msg.value * tokensPerEth;
        uint256 timedMult = calculatePercentMultiplier();
        uint256 toSend;
        if( referralAddress == address(0) ){
            toSend = (basePurchase * timedMult / 100) / 1e18; //division by 1e18 is necessary to account for difference in decimals between token and ETH
        } else {
            toSend = (basePurchase * (timedMult + referreePercent) / 100) / 1e18; //division by 1e18 is necessary to account for difference in decimals between token and ETH
            uint256 referralToSend = (basePurchase * referrerPercent / 100) / 1e18; //division by 1e18 is necessary to account for difference in decimals between token and ETH
            _transfer(owner, referralAddress, referralToSend);
            emit Referred(referralAddress, referralToSend);
        }
        _transfer(owner, msg.sender, toSend);
        emit Purchased(msg.sender, toSend);
    }

    receive() external payable {
        buyTokens(address(0));
    }

    function airdrop(address[] calldata destinations, uint256[] calldata amounts) external {
        require(destinations.length == amounts.length, "airdrop: unequal input lengths");
        for (uint256 i = 0; i < destinations.length; ++i){
            transfer(destinations[i], amounts[i]);
        }
    }

    //multiplier in percent
    function calculatePercentMultiplier() public view returns(uint256) {
        if( block.timestamp <= (startTime + 1 weeks) ){
            return 300;
        }else if( block.timestamp <= (startTime + 2 weeks) ){
            return 200;
        }else if( block.timestamp <= (startTime + 3 weeks) ){
            return 150;
        }else if( block.timestamp <= (startTime + 4 weeks) ){
            return 125;
        }else{
            return 100;
        }
    }

    function drain(address payable dest) external onlyOwner() {
        dest.transfer( address(this).balance );
    }

    function openOrCloseSale(bool argument) external onlyOwner() {
        saleOpen = argument;
    }

    function setPrice(uint256 newPrice) external onlyOwner() {
        require(newPrice != 0, "what are you, an idiot?");
        tokensPerEth = newPrice;
    }
}
function calculatePercentMultiplier() public view returns(uint256) {
    if( block.timestamp <= (startTime + 1 weeks) ){
        return 300;
    }else if( block.timestamp <= (startTime + 2 weeks) ){
        return 200;
    }else if( block.timestamp <= (startTime + 3 weeks) ){
        return 150;
    }else if( block.timestamp <= (startTime + 4 weeks) ){
        return 125;
    }else{
        return 100;
    }
}

//multiplier in percent