contract cVault is ERC20 {
    using SafeERC20 for IERC20;
    using Address for address;
    using SafeMath for uint256;

    IERC20 public token;

    uint public min = 9500;
    uint public constant max = 10000;

    address public governance;
    address public controller;
    address public stakingPool;

    constructor (address _token, address _controller)
        public ERC20(
          string(abi.encodePacked("Compounder ", ERC20(_token).name())),
          string(abi.encodePacked("c", ERC20(_token).symbol()))
        )
    {
        token = IERC20(_token);
        _setupDecimals(ERC20(_token).decimals());
        governance = msg.sender;
        controller = _controller;
    }

    // Total balance of token in both vault and corresponding strategy
    function balance() public view returns (uint) {
        return token.balanceOf(address(this))
                .add(IController(controller).balanceOf(address(token)));
    }

    function setMin(uint _min) external {
        require(msg.sender == governance, "!governance");
        min = _min;
    }

    function setGovernance(address _governance) public {
        require(msg.sender == governance, "!governance");
        governance = _governance;
    }

    function setController(address _controller) public {
        require(msg.sender == governance, "!governance");
        controller = _controller;
    }

    function setStakingPool(address _stakingPool) public {
        require(msg.sender == governance, "!governance");
        stakingPool = _stakingPool;
    }

    function infiniteApproveStakingPool() public {
        require(msg.sender == governance, "!governance");
        _approve(address(this), stakingPool, uint(-1));
    }

    function removeApprovalStakingPool() public {
        require(msg.sender == governance, "!governance");
        _approve(address(this), stakingPool, uint(0));
    }

    // Custom logic in here for how much the vault allows to be borrowed
    // Sets minimum required on-hand to keep small withdrawals cheap
    function available() public view returns (uint) {
        return token.balanceOf(address(this)).mul(min).div(max);
    }

    // Called by Keeper to put deposited token into strategy
    function earn() public {
        uint _bal = available();
        token.safeTransfer(controller, _bal);
        IController(controller).earn(address(token), _bal);
    }

    function depositAll(bool _autoStakeInStakingPool) external {
        deposit(token.balanceOf(msg.sender), _autoStakeInStakingPool);
    }

    function deposit(uint _amount, bool _autoStakeInStakingPool) public {
        uint _pool = balance();
        uint _before = token.balanceOf(address(this));
        token.safeTransferFrom(msg.sender, address(this), _amount);
        uint _after = token.balanceOf(address(this));
        _amount = _after.sub(_before); // Additional check for deflationary tokens
        uint shares = 0;
        if (totalSupply() == 0) {
            shares = _amount;
        } else {
            shares = (_amount.mul(totalSupply())).div(_pool);
        }

        if (_autoStakeInStakingPool) {
            _mint(address(this), shares);
            IStakingRewards(stakingPool).stakeFor(shares, msg.sender);
            IStakingRewards(stakingPool).getRewardFor(msg.sender);
        } else {
            _mint(msg.sender, shares);
        }
    }

    function withdrawAll() external {
        withdraw(balanceOf(msg.sender), false);
    }

    function withdrawAllFromRewardPool() external {
        withdraw(IStakingRewards(stakingPool).balanceOf(msg.sender), true);
    }

    // No rebalance implementation for lower fees and faster swaps
    function withdraw(uint _shares, bool _autoWithdrawFromStakingPool) public {
        if (_autoWithdrawFromStakingPool) {
            IStakingRewards(stakingPool).withdrawForUserByCVault(_shares, msg.sender);
            IStakingRewards(stakingPool).getRewardFor(msg.sender);
        }

        uint r = (balance().mul(_shares)).div(totalSupply());
        _burn(msg.sender, _shares);

        // Check balance
        uint b = token.balanceOf(address(this));
        if (b < r) {
            uint _withdraw = r.sub(b);
            IController(controller).withdraw(address(token), _withdraw);
            uint _after = token.balanceOf(address(this));
            uint _diff = _after.sub(b);
            if (_diff < _withdraw) {
                r = b.add(_diff);
            }
        }

        token.safeTransfer(msg.sender, r);
    }

    // Used to swap any borrowed reserve over the debt limit to liquidate to 'token'
    function harvest(address reserve, uint amount) external {
        require(msg.sender == controller, "!controller");
        require(reserve != address(token), "token");
        IERC20(reserve).safeTransfer(controller, amount);
    }

    function getPricePerFullShare() public view returns (uint) {
        return balance().mul(1e18).div(totalSupply());
    }
}
function available() public view returns (uint) {
    return token.balanceOf(address(this)).mul(min).div(max);
}

// Custom logic in here for how much the vault allows to be borrowed
// Sets minimum required on-hand to keep small withdrawals cheap