abstract contract PoolShareToken is ERC20, ReentrancyGuard, Pausable {
    uint256 public withdrawFee; // Example: 1e17 is 10%
    address public feeCollector;

    /**
     * @dev Constructor.
     */
    constructor(string memory name, string memory symbol) public ERC20(name, symbol) {}

    /**
     * @dev Receives ETH and grants new tokens to the sender depending on the
     * value of each contract's share.
     */
    function deposit() public virtual payable nonReentrant {}

    /**
     * @dev Returns the value stored in the contract. Should be overwritten if
     * it stored in another contract.
     */
    function totalValue() public virtual view returns (uint256) {}

    /**
     * @dev Burns tokens and retuns the ETH value of those.
     */
    function withdraw() public virtual {}

    /**
     * @dev Hook that is called just before minting new tokens. To be used i.e.
     * if the deposited amount is to be transferred to a different contract.
     */
    function _beforeMinting(uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called just after burning tokens. To be used i.e. if
     * the ETH stored in a different/this contract needs to be transferred.
     */
    function _afterBurning(uint256 shares) internal virtual {}

    function _deposit(uint256 amount) internal whenNotPaused {
        require(amount > 0, "Deposit must be greater than 0");

        uint256 _totalSupply = totalSupply();
        uint256 _totalValue = totalValue();
        uint256 shares = (_totalSupply == 0 || _totalValue == 0)
            ? amount
            : amount.mul(_totalSupply).div(_totalValue);
        require(_totalSupply.add(shares) < 50e18, "Test limit");
        _beforeMinting(amount);

        _mint(msg.sender, shares);
    }

    event Withdraw(address owner, uint256 shares, uint256 amount);

    /**
     * @dev Hook that is called just before burning tokens. To be used i.e. if
     * the ETH is stored in a different contract and must be withdraw from there
     * first.
     */
    function _beforeBurning(uint256 shares) internal virtual {}

    function _handleFee(uint256 shares) internal returns (uint256 _sharesAfterFee) {
        if (withdrawFee > 0) {
            uint256 _fee = shares.mul(withdrawFee).div(1e18);
            _sharesAfterFee = shares.sub(_fee);
            _transfer(_msgSender(), feeCollector, _fee);
        } else {
            _sharesAfterFee = shares;
        }
    }

    function _updateWithdrawFee(uint256 _withdrawFee) internal {
        require(_withdrawFee <= 1e18, "fee is greater than 100%");
        withdrawFee = _withdrawFee;
    }

    function _updateFeeCollector(address _feeCollector) internal {
        require(_feeCollector != address(0), "_feeCollector is 0x0");
        feeCollector = _feeCollector;
    }

    /**
     * @dev Burns tokens and retuns the collateral value, after fee, of those.
     */
    function _withdraw(uint256 shares) internal whenNotShutdown {
        require(shares > 0, "Withdraw must be greater than 0");
        uint256 sharesAfterFee = _handleFee(shares);
        uint256 amount = sharesAfterFee.mul(totalValue()).div(totalSupply());
        _burn(msg.sender, sharesAfterFee);
        _afterBurning(amount);
        emit Withdraw(msg.sender, shares, amount);
    }
}
function _beforeBurning(uint256 shares) internal virtual {}

/**
 * @dev Hook that is called just before burning tokens. To be used i.e. if
 * the ETH is stored in a different contract and must be withdraw from there
 * first.
 */