contract CarmaMaster {
    using SafeMath for uint256;

    struct UserInfo {
        uint256 stake;
        uint256 lastRewardBlock;
    }

    xCombToken public xcomb;
    CarmaToken public carma;

    uint256 MAX_INT = 115792089237316195423570985008687907853269984665640564039457584007913129639935;

    address private COMB_ETH_PAIR = address(
        0x6e168d4fD7569EA1C56d985256cd2E93ee12490e // mainnet
        // 0x39444e8Ee494c6212054CFaDF67abDBE97e70207 // ropsten
    );

    IUniswapV2ERC20 public wethToken = IUniswapV2ERC20(
        0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 // mainnet
        // 0xc778417E063141139Fce010982780140Aa0cD5Ab // ropsten
    );

    IUniswapV2ERC20 public combToken = IUniswapV2ERC20(
        0x7d36cCe46DD2B0D28dde12A859C2ACe4a21E3678 // mainnet
        // 0xb93152b59e65a6De8D3464061BcC1d68f6749F98 // ropsten
    );

    IUniswapV2Router02 private UniswapV2Router02 = IUniswapV2Router02(
        0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D //mainnet
        // 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D // ropsten
    );

    uint256 constant CARMA_PER_LP = 1693766930000000;

    uint256 public lastMintBlock = block.number;

    uint256 public totalStaked = 0;

    mapping(address => UserInfo) public users;

    constructor(CarmaToken _carma, xCombToken _xcomb) public {
        carma = _carma;
        xcomb = _xcomb;

        combToken.approve(address(UniswapV2Router02), MAX_INT);
    }

    receive() external payable {
        if (msg.sender != address(UniswapV2Router02)) {
            stake(0);
        }
    }

    // Lock ETH
    function stake(uint256 _amountOutMin) public payable {
        require(msg.value > 0, "amount can not be 0");
        require(_amountOutMin >= 0, "amount can not be negative");

        uint256 ethAmount = msg.value.div(2);

        address[] memory path = new address[](2);
        path[0] = address(wethToken);
        path[1] = address(combToken);

        UniswapV2Router02.swapExactETHForTokens{value: ethAmount}(
            _amountOutMin,
            path,
            address(this),
            block.timestamp + 60
        );
        
        uint256 amountTokenDesired = address(this).balance.mul(combToken.balanceOf(COMB_ETH_PAIR)).div(wethToken.balanceOf(COMB_ETH_PAIR));
        uint256 combBalance = combToken.balanceOf(address(this));
        if (amountTokenDesired > combBalance) {
            amountTokenDesired = combBalance;
        }

        (, , uint256 liquidity) = UniswapV2Router02.addLiquidityETH{
            value: address(this).balance
        }(
            address(combToken),
            amountTokenDesired,
            0,
            0,
            address(this),
            block.timestamp + 60
        );

        // Refund COMB if left any
        combToken.transfer(msg.sender, combToken.balanceOf(address(this)));

        // Refund ETH if left any
        msg.sender.transfer(address(this).balance);

        _addUserStake(msg.sender, liquidity);
    }

    // Lock COM-ETH LP tokens directly
    function addLPToken(uint256 _amount) public {
        require(_amount > 0, "Amount can not be 0");

        IUniswapV2ERC20(COMB_ETH_PAIR).transferFrom(
            msg.sender,
            address(this),
            _amount
        );

        _addUserStake(msg.sender, _amount);
    }

    // Get user's pending reward
    function pendingReward(address _address) public view returns (uint256) {
        UserInfo memory user = users[_address];
        return user.stake.mul(block.number.sub(user.lastRewardBlock)).mul(CARMA_PER_LP).div(1 ether);
    }

    // Mint CARMA and transfer reward
    function claimReward() external {
        _mintCarma();
        _claimReward();
    }

    // Sends Pending CARMA to the user
    function _addUserStake(address _address, uint256 _amount) internal {
        _mintCarma();

        if (users[_address].stake > 0) {
            _claimReward();
        }

        totalStaked = totalStaked.add(_amount);

        xcomb.mint(_address, _amount);
        users[_address].stake = users[_address].stake.add(_amount);
        users[_address].lastRewardBlock = block.number;
    }

    // Mints required amount of CARMA tokens to transfer to the stakers later
    function _mintCarma() internal {
        uint256 blocksPassed = block.number.sub(lastMintBlock);
        if (blocksPassed == 0) {
            return;
        }
        carma.mint(
            address(this),
            blocksPassed.mul(CARMA_PER_LP).mul(totalStaked).div(1 ether)
        );
        lastMintBlock = block.number;
    }

    // Sends Pending CARMA to the user
    function _claimReward() internal {
        _safeCarmaTransfer(msg.sender, pendingReward(msg.sender));
        users[msg.sender].lastRewardBlock = block.number;
    }

    // Safe CARMA transfer to avoid rounding errors
    function _safeCarmaTransfer(address _to, uint256 _amount) internal {
        uint256 bal = carma.balanceOf(address(this));
        if (_amount > bal) {
            carma.transfer(_to, bal);
        } else {
            carma.transfer(_to, _amount);
        }
    }
}
function _safeCarmaTransfer(address _to, uint256 _amount) internal {
        uint256 bal = carma.balanceOf(address(this));
        if (_amount > bal) {
            carma.transfer(_to, bal);
        } else {
            carma.transfer(_to, _amount);
        }
    }
// Safe CARMA transfer to avoid rounding errors