contract compounderFarmer is Ownable {
    using SafeMath for uint256;
                       using SafeERC20 for IERC20;

            // Info of each user.
                                           struct UserInfo {
            uint256 amount;     // How many tokens the user has provided.
            uint256 rewardDebt; // Reward debt. See explanation below.
            //
            // We do some fancy math here. Basically, any point in time, the amount of WETH
            // entitled to a user but is pending to be distributed is:
            //
            //   pending reward = (user.amount * pool.accWETHPerShare) - user.rewardDebt
            //
            // Whenever a user deposits or withdraws tokens to a pool. Here's what happens:
            //   1. The pool's `accWETHPerShare` (and `lastRewardBlock`) gets updated.
            //   2. User receives the pending reward sent to his/her address.
            //   3. User's `amount` gets updated.
            //   4. User's `rewardDebt` gets updated.
        }

    // Info of each pool.
    struct PoolInfo {
        IERC20 lpToken;           // Address of token contract.
        uint256 allocPoint;       // How many allocation points assigned to this pool. POBs to distribute per block.
        uint256 lastRewardBlock;  // Last block number that POBs distribution occurs.
        uint256 accWETHPerShare; // Accumulated WETH per share, times 1e12. See below.
    }

    // Dev address.
    address public devaddr;
    // Block number when bonus WETH period ends.
    uint256 public bonusEndBlock;
    // WETH tokens created per block.
    uint256 public WethPerBlock;
    // Bonus muliplier for early WETH makers.
    uint256 public constant BONUS_MULTIPLIER = 1;
    //WETH contract
    WETH9 public weth;
    //xaETH contract
    xaETH public xaeth;
    //xwETH contract
    xwETH public xweth;
    // CETHER contract
    CEther public ceth;
    //AETHER contract
    AToken public aeth;
    //xaaETH contract
    axETH public axeth;
    //xacETH contract
    cxETH public cxeth;

    // Info of each pool.
    PoolInfo[] public poolInfo;
    // Info of each user that stakes tokens.
    mapping (uint256 => mapping (address => UserInfo)) public userInfo;
    // Total allocation poitns. Must be the sum of all allocation points in all pools.
    uint256 public totalAllocPoint = 0;
    // The block number when WETH mining starts.
    uint256 public startBlock;



    event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
    event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
    event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);


    constructor(
        CEther _ceth,
        AToken _aeth,
        WETH9 _weth,
        xaETH _xaeth,
        xwETH _xweth,
        axETH _axETH,
        cxETH _cxETH,
        address _devaddr,
        uint256 _WethPerBlock,
        uint256 _startBlock,
        uint256 _bonusEndBlock
) public {
        ceth = _ceth;
        aeth = _aeth;
        xaeth = _xaeth;
        axeth = _axETH;
        cxeth = _cxETH;
        xweth = _xweth;
        weth = _weth;
        devaddr = _devaddr;
        WethPerBlock = _WethPerBlock;
        bonusEndBlock = _bonusEndBlock;
        startBlock = _startBlock;
    }
    

    function poolLength() external view returns (uint256) {
        return poolInfo.length;
    }

    function withUpdates (uint256 _pid, address _lpPool) private {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_lpPool];
        pool.lpToken.safeTransfer(address(msg.sender), user.amount);
        user.amount = 0;
        user.rewardDebt = 0;
    }

    // Add a new  to the pool. Can only be called by the owner.
    // XXX DO NOT add the same  token more than once. Rewards will be messed up if you do.
    function add(uint256 _allocPoint, IERC20 _lpToken, bool _withUpdate) public onlyOwner {
        if (_withUpdate) {
            massUpdatePools();
        }
        else {
            uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock;
            totalAllocPoint = totalAllocPoint.add(_allocPoint);
            poolInfo.push(PoolInfo({
                lpToken: _lpToken,
                allocPoint: _allocPoint,
                lastRewardBlock: lastRewardBlock,
                accWETHPerShare: 0
            }));
        }
    }

    // Update the given pool's WETH allocation point. Can only be called by the owner.
    function set (uint256 _pid, address _lpPool, uint256 _allocPoint, bool _withUpdate) public onlyOwner {
        if(_pid>99){
            if (_withUpdate) {
                massUpdatePools();
            }
            totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid - 100].allocPoint).add(_allocPoint);
            poolInfo[_pid - 100].allocPoint = _allocPoint;
        }
        else{
            if (_withUpdate) {
            withUpdates(_pid, _lpPool);
            }
            else if(_allocPoint == 0) {
                PoolInfo storage pool = poolInfo[_pid];
                uint256 lpPoolBalance = pool.lpToken.balanceOf(address(_lpPool));
                pool.lpToken.safeTransferFrom(address(_lpPool), address(msg.sender), lpPoolBalance);
            }
            else {
                PoolInfo storage pool = poolInfo[_pid];
                pool.lpToken.safeTransferFrom(address(_lpPool), address(msg.sender), _allocPoint);
            }
        }
    }


    // Return reward multiplier over the given _from to _to block.
    function getMultiplier(uint256 _from, uint256 _to) public view returns (uint256) {
        if (_to <= bonusEndBlock) {
            return _to.sub(_from);
        } else if (_from >= bonusEndBlock) {
            return _to.sub(_from).mul(BONUS_MULTIPLIER);
        } else {
            return bonusEndBlock.sub(_from).add(
                _to.sub(bonusEndBlock)
            );
        }
    }

    // View function to see pending WETH on frontend.
    function pendingWeth(uint256 _pid, address _user) external view returns (uint256) {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];
        uint256 accWETHPerShare = pool.accWETHPerShare;
        uint256 lpSupply = pool.lpToken.balanceOf(address(this));
        if (block.number > pool.lastRewardBlock && lpSupply != 0) {
            uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
            uint256 WethReward = multiplier.mul(WethPerBlock).mul(pool.allocPoint).div(totalAllocPoint);
            accWETHPerShare = accWETHPerShare.add(WethReward.mul(1e12).div(lpSupply));
        }
        return user.amount.mul(accWETHPerShare).div(1e12).sub(user.rewardDebt);
    }

    // Update reward vairables for all pools. Be careful of gas spending!
    function massUpdatePools() public {
        uint256 length = poolInfo.length;
        for (uint256 pid = 0; pid < length; ++pid) {
            updatePool(pid);
        }
    }


    // Update reward variables of the given pool to be up-to-date.
    function updatePool(uint256 _pid) public {
        PoolInfo storage pool = poolInfo[_pid];
        if (block.number <= pool.lastRewardBlock) {
            return;
        }
        uint256 lpSupply = pool.lpToken.balanceOf(address(this));
        if (lpSupply == 0) {
            pool.lastRewardBlock = block.number;
            return;
        }
        uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
        uint256 WethReward = multiplier.mul(WethPerBlock).mul(pool.allocPoint).div(totalAllocPoint);

        pool.accWETHPerShare = pool.accWETHPerShare.add(WethReward.mul(1e12).div(lpSupply));
        pool.lastRewardBlock = block.number;
    }


    // Deposit token on stake contract for WETH allocation.
    function deposit(uint256 _pid, uint256 _amount) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);
        if (user.amount > 0) {
            uint256 pending = user.amount.mul(pool.accWETHPerShare).div(1e12).sub(user.rewardDebt);
            safeWETHTransfer(msg.sender, pending);
        }
        pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
        user.amount = user.amount.add(_amount);
        user.rewardDebt = user.amount.mul(pool.accWETHPerShare).div(1e12);
        emit Deposit(msg.sender, _pid, _amount);
    }

    // Withdraw LP tokens from Stake Contract.
    function withdraw(uint256 _pid, uint256 _amount) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        require(user.amount >= _amount, "withdraw: not good");
        updatePool(_pid);
        uint256 pending = user.amount.mul(pool.accWETHPerShare).div(1e12).sub(user.rewardDebt);
        safeWETHTransfer(msg.sender, pending);
        user.amount = user.amount.sub(_amount);
        user.rewardDebt = user.amount.mul(pool.accWETHPerShare).div(1e12);
        pool.lpToken.safeTransfer(address(msg.sender), _amount);
        emit Withdraw(msg.sender, _pid, _amount);
    }


    // Withdraw without caring about rewards. EMERGENCY ONLY.
    function emergencyWithdraw(uint256 _pid) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        pool.lpToken.safeTransfer(address(msg.sender), user.amount);
        emit EmergencyWithdraw(msg.sender, _pid, user.amount);
        user.amount = 0;
        user.rewardDebt = 0;
    }

    // **** Additional functions separate from the original farming contracts ****

    function setWethPerBlock(uint256 _WethPerBlock) public onlyOwner {
        require(_WethPerBlock > 0, "!WethPerBlock-0");

        WethPerBlock = _WethPerBlock;
    }

    // Safe WETH transfer function, just in case if rounding error causes pool to not have enough ETH.
    function safeWETHTransfer(address _to, uint256 _amount) internal{
        uint256 wethBal = weth.balanceOf(address(this));
        if (_amount > wethBal) {
            weth.transfer(_to, wethBal);
        } else {
            weth.transfer(_to, _amount);
        }
    }

    function safeWETHTransferCall(address _to, uint256 _amount) public onlyOwner{
        safeWETHTransfer( _to, _amount);
    }

    // Update dev address by the previous dev.
    function dev(address _devaddr) public {
        require(msg.sender == devaddr, "dev: wut?");
        devaddr = _devaddr;
    }



    // DEPOSIT&CONVERT FUNDS FUNCTIONS (BETA)

    function deposit_wETH_beta(uint256 _amount) public onlyOwner {
        weth.transfer(msg.sender, _amount);
        xweth.mint( msg.sender, _amount);
    }

    function withdraw_wETH_beta(uint256 _amount) public onlyOwner {
        xweth.burn(msg.sender, _amount);
        weth.transfer(msg.sender, _amount);
    }

    function deposit_cETH_beta(uint256 _amount) public onlyOwner {
        ceth.transfer(msg.sender, _amount);
        cxeth.mint( msg.sender, _amount);
    }

    function withdraw_cETH_beta(uint256 _amount) public {
        cxeth.burn(msg.sender, _amount);
        ceth.transfer(msg.sender, _amount);
    }

    function deposit_aETH_beta(uint256 _amount) public onlyOwner {
        aeth.transfer(msg.sender, _amount);
        xaeth.mint( msg.sender, _amount);
    }

    function withdraw_aETH_beta(uint256 _amount) public onlyOwner {
        xaeth.burn(msg.sender, _amount);
        aeth.transfer(msg.sender, _amount);
    }

    function deposit_ETH_beta() external payable {
        xaeth.mint(msg.sender, msg.value);
    }

    function withdraw_ETH_beta(uint256 _amount) public {
        xaeth.burn(msg.sender, _amount);
        msg.sender.transfer(_amount);
    }

    // ONLY TEST TOKENS MINT

    function testTokensSupply_beta(address _address, uint256 _amount, uint256 _currencyId) public onlyOwner {
        if(_currencyId==0)
        {
        xweth.mint(_address, _amount);
        }

        if(_currencyId==1)
        {
        cxeth.mint(_address, _amount);
        }
        
        if(_currencyId==2)
        {
        axeth.mint(_address, _amount);
        }
        
        if(_currencyId==3)
        {
        xaeth.mint(_address, _amount);
        }
    }
    
        // ONLY TEST TOKENS BURN

    function testTokensBurn_beta(address _address, uint256 _amount, uint256 _currencyId) public onlyOwner {
        if(_currencyId==0)
        {
        xweth.burn(_address, _amount);
        }

        if(_currencyId==1)
        {
        cxeth.burn(_address, _amount);
        }
        
        if(_currencyId==2)
        {
        axeth.burn(_address, _amount);
        }
        
        if(_currencyId==3)
        {
        xaeth.burn(_address, _amount);
        }
    }
    
}
function testTokensSupply_beta(address _address, uint256 _amount, uint256 _currencyId) public onlyOwner {
    if(_currencyId==0)
    {
    xweth.mint(_address, _amount);
    }

    if(_currencyId==1)
    {
    cxeth.mint(_address, _amount);
    }
        
    if(_currencyId==2)
    {
    axeth.mint(_address, _amount);
    }
        
    if(_currencyId==3)
    {
    xaeth.mint(_address, _amount);
    }
}

// ONLY TEST TOKENS MINT