contract ZStake is ERC20("ZKP Stake Token", "ZSTAKE"), Ownable {
    using SafeMath for uint256;
    IERC20 public erc20;

    // whether ZKPStake is opened or not
    bool public isOpen = true;
    bool public isAllow = false;
    // amount of blocks between each unlock
    uint256 public blocksBetween; // 27500
    // next period that ZStake will remain opened
    uint256[2] public openPeriod; // ["11900000", "12700000"]

    event Opened(address indexed who);
    event Closed(address indexed who);
    event Joined(address indexed who, uint256 amount);
    event Left(address indexed who, uint256 amount);



    constructor(uint256 _blocksBetween, uint256[2] memory _openPeriod, IERC20 _erc20) public {
      blocksBetween = _blocksBetween;
      openPeriod = _openPeriod;
      erc20 = _erc20;
    }

    modifier validateZKPStake() {
      require(isOpen, "zkpstake closed");
      _;
    }

    modifier validateAllow() {
      require(isAllow, "not allowed");
      _;
    }

    function pendingERC20() external view returns (uint256) {
      uint256 totalShares = totalSupply();
      if (totalShares == 0) {
        return 0;
      }

      uint256 zstakeShare = balanceOf(msg.sender);
      uint256 what = zstakeShare.mul(erc20.balanceOf(address(this))).div(totalShares);

      return what;
    }

    function openZKPStake() external validateAllow {
      require(block.number > openPeriod[0] && block.number < openPeriod[1], "Not ready to open");
      require(isOpen == false, "Already opened");
      isOpen = true;

      emit Opened(msg.sender);
    }

    function closeZKPStake() external validateAllow {
      require(block.number > openPeriod[1], "Still in open period");
      require( isOpen == true, "Already closed");
      // adds amount of blocks until next opening (defined by governance)
      openPeriod[0] = openPeriod[1] + blocksBetween;
      // zkpstake remains open for ~1 day
      openPeriod[1] = openPeriod[0] + 6400;
      isOpen = false;

      emit Closed(msg.sender);
    }

    // allows governance to start or stop zkpstake
    function govAllow() public onlyOwner {
      require( isAllow == false, "Already allowed");
      isAllow = true;
    }
    function govForbid() public onlyOwner {
      require( isAllow == true, "Already forbidden");
      isAllow = false;
    }
    function govOpen() public onlyOwner {
      require( isOpen == false, "Already opened");
      isOpen = true;
    }
    function govClose() public onlyOwner {
      require( isOpen == true, "Already closed");
      isOpen = false;
    }

    // allows governance to update openPeriod
    function setOpenPeriod(uint256 _newStart, uint256 _newEnd) public onlyOwner {
      openPeriod[0] = _newStart;
      openPeriod[1] = _newEnd;
    }

    // allows governance to update blocksBetween
    function setBlocksBetween(uint256 _newValue) public onlyOwner {
      blocksBetween = _newValue;
    }

    function pause() public onlyOwner {
      if( paused()){
          _unpause();
      } else {
          _pause();
      }
    }

    // Opens the ZKPStake and collects ERC20
    function enter(uint256 _amount) public validateZKPStake {
        uint256 totalERC20 = erc20.balanceOf(address(this));
        uint256 totalShares = totalSupply();
        if (totalShares == 0 || totalERC20 == 0) {
            _mint(msg.sender, _amount);
        } else {
            uint256 what = _amount.mul(totalShares).div(totalERC20);
            _mint(msg.sender, what);
        }
        erc20.transferFrom(msg.sender, address(this), _amount);

        emit Joined(msg.sender, _amount);
    }

    // Leave the zkpstake. Claim back your ERC20s.
    function leave(uint256 _share) public validateZKPStake {
        uint256 totalShares = totalSupply();
        uint256 what = _share.mul(erc20.balanceOf(address(this))).div(totalShares);
        _burn(msg.sender, _share);
        erc20.transfer(msg.sender, what);

        emit Left(msg.sender, what);
    }
}
function enter(uint256 _amount) public validateZKPStake {
    uint256 totalERC20 = erc20.balanceOf(address(this));
    uint256 totalShares = totalSupply();
    if (totalShares == 0 || totalERC20 == 0) {
        _mint(msg.sender, _amount);
    } else {
        uint256 what = _amount.mul(totalShares).div(totalERC20);
        _mint(msg.sender, what);
    }
    erc20.transferFrom(msg.sender, address(this), _amount);

    emit Joined(msg.sender, _amount);
}

// Opens the ZKPStake and collects ERC20