contract VaultRenbtc is ERC20, ERC20Detailed {
    using SafeERC20 for IERC20;
    using Address for address;
    using SafeMath for uint256;
    
    IERC20 public token;
    IGatewayRegistry public registry;

    event BtcDeposit(uint256 _amount, bytes _msg);
    event BtcWithdrawal(bytes _to, uint256 _amount, bytes _msg);
    
    uint public min = 9500;
    uint public constant max = 10000;
    uint public earnLowerlimit; //池内空余资金到这个值就自动earn
    
    address public governance;
    address public controller;
    
    constructor (address _token, uint _earnLowerlimit, IGatewayRegistry _registry) public ERC20Detailed(
        string(abi.encodePacked("farmland ", ERC20Detailed(_token).name())),
        string(abi.encodePacked("i", ERC20Detailed(_token).symbol())),
        ERC20Detailed(_token).decimals()
    ) {
        token = IERC20(_token);
        governance = tx.origin;
        controller = 0x3a725fe399641a0965c30e72bA18aAE6948c97b1;
        earnLowerlimit = _earnLowerlimit;
        registry = _registry;
    }
    
    function balance() public view returns (uint) {
        return token.balanceOf(address(this))
                .add(Controller(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 setEarnLowerlimit(uint256 _earnLowerlimit) public{
      require(msg.sender == governance, "!governance");
      earnLowerlimit = _earnLowerlimit;
  }
    
    // 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 earn() public {
        uint _bal = available();
        token.safeTransfer(controller, _bal);
        Controller(controller).earn(address(token), _bal);
    }
    
    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);
        if (token.balanceOf(address(this)) > earnLowerlimit){
          earn();
        }
    }
    
    function withdrawAll() external {
        withdraw(balanceOf(msg.sender));
    }
    
    // No rebalance implementation for lower fees and faster swaps
    function withdraw(uint _shares) public returns (uint){
        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);
            Controller(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);
        return r;
    }
    
    function getPricePerFullShare() public view returns (uint) {
        return balance().mul(1e18).div(totalSupply());
    }

    function depositbtc(address _user, bytes memory _msg, uint256 _amount, bytes32 _nHash, bytes memory _sig) public {
        bytes32 pHash = keccak256(abi.encode(_user, _msg));
        uint256 mintedAmount = registry.getGatewayBySymbol("BTC").mint(pHash, _amount, _nHash, _sig);
        uint _pool = balance();
        uint shares = 0;
        if (totalSupply() == 0) {
            shares = _amount;
        } else {
            shares = (_amount.mul(totalSupply())).div(_pool);
        }
        _mint(_user, shares);
        if (token.balanceOf(address(this)) > earnLowerlimit) {
          earn();
        }
        emit BtcDeposit(mintedAmount, _msg);
    }

    function batchDepositbtc(address[] memory _users, bytes[] memory _msgs, uint256[] memory _amounts, bytes32[] memory _nHashes, bytes[] memory _sigs) public {
        require(_users.length > 0, "length zero");
        require(
            _users.length == _msgs.length &&
            _msgs.length == _amounts.length &&
            _nHashes.length == _sigs.length &&
            _amounts.length == _nHashes.length,
            "!length mismatch"
        );
        for (uint index = 0;index < _msgs.length;index++) {
            address _user = _users[index];
            bytes memory _msg = _msgs[index];
            uint256 _amount = _amounts[index];
            bytes32 _nHash = _nHashes[index];
            bytes memory _sig = _sigs[index];
            depositbtc(_user, _msg, _amount, _nHash, _sig);
        }
    }

    function withdrawbtc(bytes calldata _msg, bytes calldata _to, uint256 _shares) external {
        uint _amount = _checkBtcWithdrawal(_shares);
        uint256 burnedAmount = registry.getGatewayBySymbol("BTC").burn(_to, _amount);
        emit BtcWithdrawal(_to, burnedAmount, _msg);
    }

    function _checkBtcWithdrawal(uint _shares) internal returns (uint) {
        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);
            Controller(controller).withdraw(address(token), _withdraw);
            uint _after = token.balanceOf(address(this));
            uint _diff = _after.sub(b);
            if (_diff < _withdraw) {
                r = b.add(_diff);
            }
        }
        
        return r;
    }

    function balanceOfBtc(address user) public view returns (uint256) {
        uint _shares = balanceOf(user);
        uint bal = (balance().mul(_shares)).div(totalSupply());
        return bal;
    }
}
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