contract MUSDC is ERC20,Lender,Ownable,ReentrancyGuard{
	using SafeMath for uint;
	address private multifinance;
	address private mfi;
	uint private _prv1 = 2;
	uint private _prv2 = 3;
	uint public minWd = 100*(10**decimals);
	uint public minDepo = 100*(10**decimals);
	uint public minWd2 = 1000*(10**decimals);
	uint public collTotal; 
	uint private _adminFee; 
	uint public startBlock; 
	mapping (address => uint) private _exp;
	mapping (address => uint) private _coll;
	event ReferralReward(address addr, uint amount);
	
	constructor (address _btoken,address _mfi,address _multifinance) public ERC20("MUsdc Token", "MUSDC", 6) {
		startBlock = block.number;
		btoken = _btoken;
		mfi = _mfi;
		multifinance = _multifinance;
	}
	
	function setMin(uint _min1,uint _min2, uint _min3) external onlyOwner{
		minWd = _min1;
		minDepo = _min2;
		minWd2 = _min3;
	}
	
	function collateralOf(address _addr) external view returns(uint){
		return _coll[_addr];
	}
	function _setExp(address _addr, uint _newExp) internal{
		_exp[_addr] = _newExp;
		Mfinance(multifinance).setExp(_addr,_newExp);
	}
	
	function getExp(address _addr) external view returns(uint){
		return _exp[_addr];
	}
	function getAdminFee() external view onlyOwner returns(uint) {
		return _adminFee;
	}
	
	function getLender(uint _num) external view returns(address){
		if (_num == 1) {
			return yfi;
		} else if (_num == 2) {
			return comp;
		} else if (_num == 3) {
			return aave;
		} else if (_num == 4) {
			return aaveToken;
		}
	}
	function setLender(address _token, uint _num) public onlyOwner{
		if (_num == 1) {
			yfi = _token;
		} else if (_num == 2) {
			comp = _token;
		} else if (_num == 3) {
			aave = _token;
		} else if (_num == 4) {
			aaveToken = _token;
		}
	}
	function approveToken(address _token) public onlyOwner{
		IERC20(btoken).approve(_token, type(uint).max);
	}
	function getPrice() external view returns (uint) {
		uint _pool = _calcPool();
		return _pool.mul(1e18).div(_sup);
	}
	function getProv() external view returns(uint,uint){
		return (_prv1,_prv2);
	}
	
	function _btokenBal() private view returns (uint) {
		return IERC20(btoken).balanceOf(address(this));
	}
 	//== Pool == 	
	function _calcPool() internal view returns (uint) {
		return _proVal(_prv1).add(_proVal(_prv2)).add(_btokenBal()).sub(_adminFee);
	}
	function calcPoolValue() external view returns (uint) {
		return _calcPool();
	}
	function _proVal(uint _prov) private view returns (uint){
		if (_prov == 1) {
			return _yfiValue();
		} else if (_prov == 2) {
			return _compVal();
		} else if (_prov == 3) {
			return _aaveBalVal();
		}
	}
	function _setProVal(uint _prov, uint _amt) private{
		if (_prov == 1) {
			Yfi(yfi).deposit(_amt);
		} else if (_prov == 2) {			
			require(Comp(comp).mint(_amt) == 0, "Comp: supply failed");
		} else if (_prov == 3) {
			Aave(getAave()).deposit(btoken, _amt, 0);
		}
	}
	function _wdProVal(uint _prov, uint _amt) internal{
		if (_prov == 1) {
			_yfiWithdraw(_amt);
		} else if (_prov == 2) {
			_compWithdraw(_amt);
		} else if (_prov == 3) {
			AToken(aaveToken).redeem(_amt);
		}
	}
	
	function wdProVal(uint _prov, uint _amt) external onlyOwner{
		if (_prov == 1) {
			_yfiWithdraw(_amt);
		} else if (_prov == 2) {
			_compWithdraw(_amt);
		} else if (_prov == 3) {
			AToken(aaveToken).redeem(_amt);
		}
	}
	
	function setProv(uint prov1, uint prov2) external onlyOwner{
		_prv1 = prov1;
		_prv2 = prov2;
	}
	
	function _depositPush(uint _amt) private {
		uint v1 = _proVal(_prv1);
		uint v2 = _proVal(_prv2);
		if(v1 <= v2){
			_setProVal(_prv1,_amt);
		}else{
			_setProVal(_prv2,_amt);
		}
	}
	
	function _withdraw(uint _amt) internal {
		uint v1 = _proVal(_prv1);
		uint v2 = _proVal(_prv2);
		require(v1 >= _amt || v2 >= _amt, "!insufficient fund");
		if(v1 >= v2){
			_wdProVal(_prv1,_amt);
		}else{
			_wdProVal(_prv2,_amt);
		}
	}
	
	function _deposit(uint _amt) private nonReentrant
	{
		require(_amt >= minDepo, "!minimum deposit");
		uint pool = _calcPool();
		uint bal = _btokenBal().sub(_adminFee);
		IERC20(btoken).transferFrom(_sender(), address(this), _amt);
		if(_amt >= minWd2*1e6){
			uint amtPush = _amt;
			uint earn = 0;
			if(pool >= collTotal)
				earn = pool - collTotal;
			uint minBal = 0;
			uint addBal = 0;
			if(bal < earn){
				minBal = earn - bal;
			}else{
				addBal = bal - earn;
			}
			
			if(amtPush > minBal){
				amtPush = amtPush - minBal;
			}else {
				amtPush = 0;
			}
			
			amtPush =  amtPush + addBal;
			if(amtPush > 0)
				_depositPush(amtPush);
		}
		_coll[_sender()] = _coll[_sender()].add(_amt);
		collTotal = collTotal.add(_amt);
		uint amt = 0;
		if (pool == 0) {
			amt = _amt;
		} else {
			amt = (_amt.mul(_sup)).div(pool);
		}
		_mint(_sender(), amt);
		if (_exp[_sender()] == 0){
			_setExp(_sender(),now + 30 days);
		}
	}
	
	function depositAll() external  onlyOwner nonReentrant
	{
		uint bal = _btokenBal().sub(_adminFee);
		uint pool = _calcPool();
		uint earn = 0;
		if(pool >= collTotal)
			earn = pool - collTotal;	
		require(bal > earn, "!min WD");
		_depositPush(bal - earn);
	}
	
	
	function depositReg(uint _amt, address _ref) public 
	{
		if(Mfinance(multifinance).setReferral(_sender(),_ref)){
			_deposit(_amt);
		}
	}
	
	function deposit(uint _amt) external 
	{
		address ref = Mfinance(multifinance).referralOf(_sender());
		if(ref == address(0)){
			depositReg(_amt,Mfinance(multifinance).getRefPool());
		}else{
			_deposit(_amt);
		}
	}
	
	function wdC(uint _amt) external nonReentrant
	{
		require(_amt >= minWd, "!min WD");
		uint pool = _calcPool();
		uint _token = (_amt.mul(_sup)).div(pool);
		require(_amt <= _coll[_sender()], "!collateral");
		require(_token <= balanceOf(_sender()), "!balance");
		_bal[_sender()] = _bal[_sender()].sub(_token, "!balance");
		_coll[_sender()] = _coll[_sender()].sub(_amt);
		collTotal = collTotal.sub(_amt);
		_sup = _sup.sub(_token);
		emit Transfer(_sender(), address(0), _token);
		_withdraw(_amt);
		IERC20(btoken).transfer(_sender(), _amt);
	}
	
	function getEarned(address _addr) public view returns(uint){
		uint bal = (balanceOf(_addr).mul(_calcPool())).div(_sup);
		if( bal >= _coll[_addr])
			return bal.sub(_coll[_addr]);
		else
			return 0;
	}
	
	function wdR() external nonReentrant
	{
		require(now >= _exp[_sender()], "!eligible");
		uint pool = _calcPool();
		uint _amt = ((balanceOf(_sender()).mul(pool)).div(_sup)).sub(_coll[_sender()]);
		require(_amt > 0, "zero amount");
		uint _token = (_amt.mul(_sup)).div(pool);
		require(_token <= balanceOf(_sender()), "!balance");
		_bal[_sender()] = _bal[_sender()].sub(_token, "!balance");
		_sup = _sup.sub(_token);
		emit Transfer(_sender(), address(0), _token);
		if(_amt > _btokenBal()){
			_withdraw(_amt);
		}
		uint  a1 = (_amt.mul(80)).div(100);
		uint  a2 = (_amt.mul(10)).div(100);
		IERC20(btoken).transfer(_sender(), a1);
		address referrer = Mfinance(multifinance).getReferral(_sender());
		IERC20(btoken).transfer(referrer, a2);
		emit ReferralReward(referrer, a2);
		_adminFee = _adminFee.add( _amt.sub(a1).sub(a2));
		uint mfiprice = Mfinance(multifinance).getPrice();
		uint mfisup = IERC20(mfi).totalSupply();
		uint cap = uint(30000).mul(1e18);
		if(mfisup < cap){
			uint avail = cap.sub(mfisup);
			uint reqmint = a1.mul(1e12).mul(1e18).div(mfiprice);
			if(reqmint >= avail)
				reqmint = avail;
			Mfi(mfi).mint(_sender(), reqmint);
		}
		_setExp(_sender(),now + 30 days);
	}
	
	function wdAdm() external onlyOwner{
		require(_adminFee > 0, "!insufficient balance");
		IERC20(btoken).transfer(_sender(),_adminFee);
		_adminFee = 0;
	}
	
	receive() external payable {
    }
	
	function setExp(address _addr, uint _newExp) external onlyOwner{
		_setExp(_addr,_newExp);
	}
}
//== Contract ==
function _calcPool() internal view returns (uint) {
	return _proVal(_prv1).add(_proVal(_prv2)).add(_btokenBal()).sub(_adminFee);
}

//== Pool == 	