contract FairLaunchCapitalVault is ERC20, ERC20Detailed {
    using SafeERC20 for IERC20;
    using Address for address;
    using SafeMath for uint256;

    IERC20 public token;

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

    address public governance;

    AaveCollateralVaultProxy constant public vaults = AaveCollateralVaultProxy(0xf0988322B8392245d6232E520BF3Cdf912b043C4);
    address constant public usdc = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
    address public vault;

    constructor (address _token) public ERC20Detailed(
        string(abi.encodePacked("flc ", ERC20Detailed(_token).name())),
        string(abi.encodePacked("flc", ERC20Detailed(_token).symbol())),
        ERC20Detailed(_token).decimals()
    ) {
        vault = vaults.deployVault(usdc);
        vaults.setModel(vault, 1);
        token = IERC20(_token);
        governance = msg.sender;
    }

    function balance() public view returns (uint) {
        return token.balanceOf(address(this))
        .add(token.balanceOf(address(vault)));
    }

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

    function credit() external {
        token.safeApprove(address(vaults), 0);
        token.safeApprove(address(vaults), token.balanceOf(address(this)));
        vaults.deposit(vault, address(token), token.balanceOf(address(this)));
    }

    function increaseLimit(address recipient, uint value) external {
        require(msg.sender == governance, "!governance");
        vaults.increaseLimit(vault, recipient, value);
    }
    function decreaseLimit(address recipient, uint value) external {
        require(msg.sender == governance, "!governance");
        vaults.decreaseLimit(vault, recipient, value);
    }
    function repay(IERC20 reserve, uint amount) external {
        reserve.safeTransferFrom(msg.sender, address(this), amount);
        reserve.safeApprove(address(vaults), 0);
        reserve.safeApprove(address(vaults), reserve.balanceOf(address(this)));
        vaults.repay(vault, address(reserve), amount);
    }

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

    // 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);
    }

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

    function deposit(uint _amount) 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);
        }
        _mint(msg.sender, shares);
    }

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

    function manage(address reserve, uint amount) external {
        require(msg.sender == governance, "!governance");
        vaults.withdraw(vault, reserve, amount);
    }

    function _withdraw(uint _amount) internal {
        vaults.withdraw(vault, address(token), _amount);
    }

    // No rebalance implementation for lower fees and faster swaps
    function withdraw(uint _shares) public {
        uint r = (balance().mul(_shares)).div(totalSupply());
        _burn(msg.sender, _shares);

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

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

    function getPricePerFullShare() public view returns (uint) {
        return balance().mul(1e18).div(totalSupply());
    }
}
function withdraw(uint _shares) public {
    uint r = (balance().mul(_shares)).div(totalSupply());
    _burn(msg.sender, _shares);

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

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

// No rebalance implementation for lower fees and faster swaps