// File: contracts/ALCXRewarder.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "./interfaces/sushi/IRewarder.sol";
import "@boringcrypto/boring-solidity/contracts/libraries/BoringERC20.sol";
import "@boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol";
import "@boringcrypto/boring-solidity/contracts/BoringOwnable.sol";
import "./MasterChefV2.sol";

/// @author @0xKeno
contract ALCXRewarder is IRewarder,  BoringOwnable{
    using BoringMath for uint256;
    using BoringMath128 for uint128;
    using BoringERC20 for IERC20;

    IERC20 private immutable rewardToken;

    /// @notice Info of each MCV2 user.
    /// `amount` LP token amount the user has provided.
    /// `rewardDebt` The amount of SUSHI entitled to the user.
    struct UserInfo {
        uint256 amount;
        uint256 rewardDebt;
    }

    /// @notice Info of each MCV2 pool.
    /// `allocPoint` The amount of allocation points assigned to the pool.
    /// Also known as the amount of SUSHI to distribute per block.
    struct PoolInfo {
        uint128 accSushiPerShare;
        uint64 lastRewardBlock;
        uint64 allocPoint;
    }

    /// @notice Info of each pool.
    mapping (uint256 => PoolInfo) public poolInfo;

    uint256[] public poolIds;

    /// @notice Info of each user that stakes LP tokens.
    mapping (uint256 => mapping (address => UserInfo)) public userInfo;
    /// @dev Total allocation points. Must be the sum of all allocation points in all pools.
    uint256 totalAllocPoint;

    uint256 public tokenPerBlock;
    uint256 private constant ACC_TOKEN_PRECISION = 1e12;

    address private immutable MASTERCHEF_V2;

    event LogOnReward(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);
    event LogPoolAddition(uint256 indexed pid, uint256 allocPoint);
    event LogSetPool(uint256 indexed pid, uint256 allocPoint);
    event LogUpdatePool(uint256 indexed pid, uint64 lastRewardBlock, uint256 lpSupply, uint256 accSushiPerShare);
    event LogInit();
    event RewardRateUpdated(uint256 oldRate, uint256 newRate);

    constructor (IERC20 _rewardToken, uint256 _tokenPerBlock, address _MASTERCHEF_V2) public {
        rewardToken = _rewardToken;
        tokenPerBlock = _tokenPerBlock;
        MASTERCHEF_V2 = _MASTERCHEF_V2;
    }


    function onSushiReward (uint256 pid, address _user, address to, uint256, uint256 lpToken) onlyMCV2 override external {
        PoolInfo memory pool = updatePool(pid);
        UserInfo storage user = userInfo[pid][_user];
        uint256 pending;
        if (user.amount > 0) {
            pending =
                (user.amount.mul(pool.accSushiPerShare) / ACC_TOKEN_PRECISION).sub(
                    user.rewardDebt
                );
            rewardToken.safeTransfer(to, pending);
        }
        user.amount = lpToken;
        user.rewardDebt = lpToken.mul(pool.accSushiPerShare) / ACC_TOKEN_PRECISION;
        emit LogOnReward(_user, pid, pending, to);
    }
    
    function pendingTokens(uint256 pid, address user, uint256) override external view returns (IERC20[] memory rewardTokens, uint256[] memory rewardAmounts) {
        IERC20[] memory _rewardTokens = new IERC20[](1);
        _rewardTokens[0] = (rewardToken);
        uint256[] memory _rewardAmounts = new uint256[](1);
        _rewardAmounts[0] = pendingToken(pid, user);
        return (_rewardTokens, _rewardAmounts);
    }

    modifier onlyMCV2 {
        require(
            msg.sender == MASTERCHEF_V2,
            "Only MCV2 can call this function."
        );
        _;
    }

    /// @notice Returns the number of MCV2 pools.
    function poolLength() public view returns (uint256 pools) {
        pools = poolIds.length;
    }

    /// @notice Add a new LP to the pool.  Can only be called by the owner.
    /// DO NOT add the same LP token more than once. Rewards will be messed up if you do.
    /// @param allocPoint AP of the new pool.
    /// @param _pid Pid on MCV2
    function add(uint256 allocPoint, uint256 _pid) public onlyOwner {
        require(poolInfo[_pid].lastRewardBlock == 0, "Pool already exists");
        uint256 lastRewardBlock = block.number;
        totalAllocPoint = totalAllocPoint.add(allocPoint);

        poolInfo[_pid] = PoolInfo({
            allocPoint: allocPoint.to64(),
            lastRewardBlock: lastRewardBlock.to64(),
            accSushiPerShare: 0
        });
        poolIds.push(_pid);
        emit LogPoolAddition(_pid, allocPoint);
    }

    /// @notice Update the given pool's SUSHI allocation point and `IRewarder` contract. Can only be called by the owner.
    /// @param _pid The index of the pool. See `poolInfo`.
    /// @param _allocPoint New AP of the pool.
    function set(uint256 _pid, uint256 _allocPoint) public onlyOwner {
        totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);
        poolInfo[_pid].allocPoint = _allocPoint.to64();
        emit LogSetPool(_pid, _allocPoint);
    }

    /// @notice View function to see pending Token
    /// @param _pid The index of the pool. See `poolInfo`.
    /// @param _user Address of user.
    /// @return pending SUSHI reward for a given user.
    function pendingToken(uint256 _pid, address _user) public view returns (uint256 pending) {
        PoolInfo memory pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];
        uint256 accSushiPerShare = pool.accSushiPerShare;
        uint256 lpSupply = MasterChefV2(MASTERCHEF_V2).lpToken(_pid).balanceOf(MASTERCHEF_V2);
        if (block.number > pool.lastRewardBlock && lpSupply != 0) {
            uint256 blocks = block.number.sub(pool.lastRewardBlock);
            uint256 sushiReward = blocks.mul(tokenPerBlock).mul(pool.allocPoint) / totalAllocPoint;
            accSushiPerShare = accSushiPerShare.add(sushiReward.mul(ACC_TOKEN_PRECISION) / lpSupply);
        }
        pending = (user.amount.mul(accSushiPerShare) / ACC_TOKEN_PRECISION).sub(user.rewardDebt);
    }

    /// @notice Update reward variables for all pools. Be careful of gas spending!
    /// @param pids Pool IDs of all to be updated. Make sure to update all active pools.
    function massUpdatePools(uint256[] calldata pids) public {
        uint256 len = pids.length;
        for (uint256 i = 0; i < len; ++i) {
            updatePool(pids[i]);
        }
    }

    /// @notice Update reward variables of the given pool.
    /// @param pid The index of the pool. See `poolInfo`.
    /// @return pool Returns the pool that was updated.
    function updatePool(uint256 pid) public returns (PoolInfo memory pool) {
        pool = poolInfo[pid];
        require(pool.lastRewardBlock != 0, "Pool does not exist");
        if (block.number > pool.lastRewardBlock) {
            uint256 lpSupply = MasterChefV2(MASTERCHEF_V2).lpToken(pid).balanceOf(MASTERCHEF_V2);

            if (lpSupply > 0) {
                uint256 blocks = block.number.sub(pool.lastRewardBlock);
                uint256 sushiReward = blocks.mul(tokenPerBlock).mul(pool.allocPoint) / totalAllocPoint;
                pool.accSushiPerShare = pool.accSushiPerShare.add((sushiReward.mul(ACC_TOKEN_PRECISION) / lpSupply).to128());
            }
            pool.lastRewardBlock = block.number.to64();
            poolInfo[pid] = pool;
            emit LogUpdatePool(pid, pool.lastRewardBlock, lpSupply, pool.accSushiPerShare);
        }
    }

    /// @dev Sets the distribution reward rate. This will also update all of the pools.
	/// @param _tokenPerBlock The number of tokens to distribute per block
	function setRewardRate(uint256 _tokenPerBlock, uint256[] calldata _pids) external onlyOwner {
		massUpdatePools(_pids);

		uint256 oldRate = tokenPerBlock;
		tokenPerBlock = _tokenPerBlock;

		emit RewardRateUpdated(oldRate, _tokenPerBlock);
	}
}
// File: contracts/interfaces/sushi/IRewarder.sol



pragma solidity 0.6.12;
import "@boringcrypto/boring-solidity/contracts/libraries/BoringERC20.sol";
interface IRewarder {
    using BoringERC20 for IERC20;
    function onSushiReward(uint256 pid, address user, address recipient, uint256 sushiAmount, uint256 newLpAmount) external;
    function pendingTokens(uint256 pid, address user, uint256 sushiAmount) external view returns (IERC20[] memory, uint256[] memory);
}
// File: @boringcrypto/boring-solidity/contracts/libraries/BoringERC20.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.6.12;

import "../interfaces/IERC20.sol";

library BoringERC20 {
    function safeSymbol(IERC20 token) internal view returns(string memory) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(0x95d89b41));
        return success && data.length > 0 ? abi.decode(data, (string)) : "???";
    }

    function safeName(IERC20 token) internal view returns(string memory) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(0x06fdde03));
        return success && data.length > 0 ? abi.decode(data, (string)) : "???";
    }

    function safeDecimals(IERC20 token) internal view returns (uint8) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(0x313ce567));
        return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
    }

    function safeTransfer(IERC20 token, address to, uint256 amount) internal {
        (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(0xa9059cbb, to, amount));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: Transfer failed");
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
        (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(0x23b872dd, from, to, amount));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: TransferFrom failed");
    }
}
// File: @boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
// a library for performing overflow-safe math, updated with awesomeness from of DappHub (https://github.com/dapphub/ds-math)
library BoringMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256 c) {require((c = a + b) >= b, "BoringMath: Add Overflow");}
    function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {require((c = a - b) <= a, "BoringMath: Underflow");}
    function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {require(b == 0 || (c = a * b)/b == a, "BoringMath: Mul Overflow");}
    function to128(uint256 a) internal pure returns (uint128 c) {
        require(a <= uint128(-1), "BoringMath: uint128 Overflow");
        c = uint128(a);
    }
    function to64(uint256 a) internal pure returns (uint64 c) {
        require(a <= uint64(-1), "BoringMath: uint64 Overflow");
        c = uint64(a);
    }
    function to32(uint256 a) internal pure returns (uint32 c) {
        require(a <= uint32(-1), "BoringMath: uint32 Overflow");
        c = uint32(a);
    }
}

library BoringMath128 {
    function add(uint128 a, uint128 b) internal pure returns (uint128 c) {require((c = a + b) >= b, "BoringMath: Add Overflow");}
    function sub(uint128 a, uint128 b) internal pure returns (uint128 c) {require((c = a - b) <= a, "BoringMath: Underflow");}
}

library BoringMath64 {
    function add(uint64 a, uint64 b) internal pure returns (uint64 c) {require((c = a + b) >= b, "BoringMath: Add Overflow");}
    function sub(uint64 a, uint64 b) internal pure returns (uint64 c) {require((c = a - b) <= a, "BoringMath: Underflow");}
}

library BoringMath32 {
    function add(uint32 a, uint32 b) internal pure returns (uint32 c) {require((c = a + b) >= b, "BoringMath: Add Overflow");}
    function sub(uint32 a, uint32 b) internal pure returns (uint32 c) {require((c = a - b) <= a, "BoringMath: Underflow");}
}
// File: @boringcrypto/boring-solidity/contracts/BoringOwnable.sol



// SPDX-License-Identifier: MIT
// Audit on 5-Jan-2021 by Keno and BoringCrypto

// P1 - P3: OK
pragma solidity 0.6.12;

// Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol + Claimable.sol
// Edited by BoringCrypto

// T1 - T4: OK
contract BoringOwnableData {
    // V1 - V5: OK
    address public owner;
    // V1 - V5: OK
    address public pendingOwner;
}

// T1 - T4: OK
contract BoringOwnable is BoringOwnableData {
    // E1: OK
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    constructor () public {
        owner = msg.sender;
        emit OwnershipTransferred(address(0), msg.sender);
    }

    // F1 - F9: OK
    // C1 - C21: OK
    function transferOwnership(address newOwner, bool direct, bool renounce) public onlyOwner {
        if (direct) {
            // Checks
            require(newOwner != address(0) || renounce, "Ownable: zero address");

            // Effects
            emit OwnershipTransferred(owner, newOwner);
            owner = newOwner;
            pendingOwner = address(0);
        } else {
            // Effects
            pendingOwner = newOwner;
        }
    }

    // F1 - F9: OK
    // C1 - C21: OK
    function claimOwnership() public {
        address _pendingOwner = pendingOwner;
        
        // Checks
        require(msg.sender == _pendingOwner, "Ownable: caller != pending owner");

        // Effects
        emit OwnershipTransferred(owner, _pendingOwner);
        owner = _pendingOwner;
        pendingOwner = address(0);
    }

    // M1 - M5: OK
    // C1 - C21: OK
    modifier onlyOwner() {
        require(msg.sender == owner, "Ownable: caller is not the owner");
        _;
    }
}
// File: contracts/MasterChefV2.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import "@boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol";
import "@boringcrypto/boring-solidity/contracts/BoringBatchable.sol";
import "@boringcrypto/boring-solidity/contracts/BoringOwnable.sol";
import "./libraries/math/SignedSafeMath.sol";
import "./interfaces/sushi/IRewarder.sol";
import "./interfaces/sushi/IMasterChef.sol";

interface IMigratorChef {
	// Take the current LP token address and return the new LP token address.
	// Migrator should have full access to the caller's LP token.
	function migrate(IERC20 token) external returns (IERC20);
}

/// @notice The (older) MasterChef contract gives out a constant number of SUSHI tokens per block.
/// It is the only address with minting rights for SUSHI.
/// The idea for this MasterChef V2 (MCV2) contract is therefore to be the owner of a dummy token
/// that is deposited into the MasterChef V1 (MCV1) contract.
/// The allocation point for this pool on MCV1 is the total allocation point for all pools that receive double incentives.
contract MasterChefV2 is BoringOwnable {
	using BoringMath for uint256;
	using BoringMath128 for uint128;
	using BoringERC20 for IERC20;
	using SignedSafeMath for int256;

	/// @notice Info of each MCV2 user.
	/// `amount` LP token amount the user has provided.
	/// `rewardDebt` The amount of SUSHI entitled to the user.
	struct UserInfo {
		uint256 amount;
		int256 rewardDebt;
	}

	/// @notice Info of each MCV2 pool.
	/// `allocPoint` The amount of allocation points assigned to the pool.
	/// Also known as the amount of SUSHI to distribute per block.
	struct PoolInfo {
		uint128 accSushiPerShare;
		uint64 lastRewardBlock;
		uint64 allocPoint;
	}

	/// @notice Address of MCV1 contract.
	IMasterChef public immutable MASTER_CHEF;
	/// @notice Address of SUSHI contract.
	IERC20 public immutable SUSHI;
	/// @notice The index of MCV2 master pool in MCV1.
	uint256 public immutable MASTER_PID;
	// @notice The migrator contract. It has a lot of power. Can only be set through governance (owner).
	IMigratorChef public migrator;

	/// @notice Info of each MCV2 pool.
	PoolInfo[] public poolInfo;
	/// @notice Address of the LP token for each MCV2 pool.
	IERC20[] public lpToken;
	/// @notice Address of each `IRewarder` contract in MCV2.
	IRewarder[] public rewarder;

	/// @notice Info of each user that stakes LP tokens.
	mapping(uint256 => mapping(address => UserInfo)) public userInfo;
	/// @dev Total allocation points. Must be the sum of all allocation points in all pools.
	uint256 public totalAllocPoint;

	uint256 private constant MASTERCHEF_SUSHI_PER_BLOCK = 1e20;
	uint256 private constant ACC_SUSHI_PRECISION = 1e12;

	event Deposit(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);
	event Withdraw(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);
	event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);
	event Harvest(address indexed user, uint256 indexed pid, uint256 amount);
	event LogPoolAddition(uint256 indexed pid, uint256 allocPoint, IERC20 indexed lpToken, IRewarder indexed rewarder);
	event LogSetPool(uint256 indexed pid, uint256 allocPoint, IRewarder indexed rewarder, bool overwrite);
	event LogUpdatePool(uint256 indexed pid, uint64 lastRewardBlock, uint256 lpSupply, uint256 accSushiPerShare);
	event LogInit();

	/// @param _MASTER_CHEF The SushiSwap MCV1 contract address.
	/// @param _sushi The SUSHI token contract address.
	/// @param _MASTER_PID The pool ID of the dummy token on the base MCV1 contract.
	constructor(
		IMasterChef _MASTER_CHEF,
		IERC20 _sushi,
		uint256 _MASTER_PID
	) public {
		MASTER_CHEF = _MASTER_CHEF;
		SUSHI = _sushi;
		MASTER_PID = _MASTER_PID;
	}

	/// @notice Deposits a dummy token to `MASTER_CHEF` MCV1. This is required because MCV1 holds the minting rights for SUSHI.
	/// Any balance of transaction sender in `dummyToken` is transferred.
	/// The allocation point for the pool on MCV1 is the total allocation point for all pools that receive double incentives.
	/// @param dummyToken The address of the ERC-20 token to deposit into MCV1.
	function init(IERC20 dummyToken) external {
		uint256 balance = dummyToken.balanceOf(msg.sender);
		require(balance != 0, "MasterChefV2: Balance must exceed 0");
		dummyToken.safeTransferFrom(msg.sender, address(this), balance);
		dummyToken.approve(address(MASTER_CHEF), balance);
		MASTER_CHEF.deposit(MASTER_PID, balance);
		emit LogInit();
	}

	/// @notice Returns the number of MCV2 pools.
	function poolLength() public view returns (uint256 pools) {
		pools = poolInfo.length;
	}

	/// @notice Add a new LP to the pool. Can only be called by the owner.
	/// DO NOT add the same LP token more than once. Rewards will be messed up if you do.
	/// @param allocPoint AP of the new pool.
	/// @param _lpToken Address of the LP ERC-20 token.
	/// @param _rewarder Address of the rewarder delegate.
	function add(
		uint256 allocPoint,
		IERC20 _lpToken,
		IRewarder _rewarder
	) public onlyOwner {
		uint256 lastRewardBlock = block.number;
		totalAllocPoint = totalAllocPoint.add(allocPoint);
		lpToken.push(_lpToken);
		rewarder.push(_rewarder);

		poolInfo.push(
			PoolInfo({ allocPoint: allocPoint.to64(), lastRewardBlock: lastRewardBlock.to64(), accSushiPerShare: 0 })
		);
		emit LogPoolAddition(lpToken.length.sub(1), allocPoint, _lpToken, _rewarder);
	}

	/// @notice Update the given pool's SUSHI allocation point and `IRewarder` contract. Can only be called by the owner.
	/// @param _pid The index of the pool. See `poolInfo`.
	/// @param _allocPoint New AP of the pool.
	/// @param _rewarder Address of the rewarder delegate.
	/// @param overwrite True if _rewarder should be `set`. Otherwise `_rewarder` is ignored.
	function set(
		uint256 _pid,
		uint256 _allocPoint,
		IRewarder _rewarder,
		bool overwrite
	) public onlyOwner {
		totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);
		poolInfo[_pid].allocPoint = _allocPoint.to64();
		if (overwrite) {
			rewarder[_pid] = _rewarder;
		}
		emit LogSetPool(_pid, _allocPoint, overwrite ? _rewarder : rewarder[_pid], overwrite);
	}

	/// @notice Set the `migrator` contract. Can only be called by the owner.
	/// @param _migrator The contract address to set.
	function setMigrator(IMigratorChef _migrator) public onlyOwner {
		migrator = _migrator;
	}

	/// @notice Migrate LP token to another LP contract through the `migrator` contract.
	/// @param _pid The index of the pool. See `poolInfo`.
	function migrate(uint256 _pid) public {
		require(address(migrator) != address(0), "MasterChefV2: no migrator set");
		IERC20 _lpToken = lpToken[_pid];
		uint256 bal = _lpToken.balanceOf(address(this));
		_lpToken.approve(address(migrator), bal);
		IERC20 newLpToken = migrator.migrate(_lpToken);
		require(bal == newLpToken.balanceOf(address(this)), "MasterChefV2: migrated balance must match");
		lpToken[_pid] = newLpToken;
	}

	/// @notice View function to see pending SUSHI on frontend.
	/// @param _pid The index of the pool. See `poolInfo`.
	/// @param _user Address of user.
	/// @return pending SUSHI reward for a given user.
	function pendingSushi(uint256 _pid, address _user) external view returns (uint256 pending) {
		PoolInfo memory pool = poolInfo[_pid];
		UserInfo storage user = userInfo[_pid][_user];
		uint256 accSushiPerShare = pool.accSushiPerShare;
		uint256 lpSupply = lpToken[_pid].balanceOf(address(this));
		if (block.number > pool.lastRewardBlock && lpSupply != 0) {
			uint256 blocks = block.number.sub(pool.lastRewardBlock);
			uint256 sushiReward = blocks.mul(sushiPerBlock()).mul(pool.allocPoint) / totalAllocPoint;
			accSushiPerShare = accSushiPerShare.add(sushiReward.mul(ACC_SUSHI_PRECISION) / lpSupply);
		}
		pending = int256(user.amount.mul(accSushiPerShare) / ACC_SUSHI_PRECISION).sub(user.rewardDebt).toUInt256();
	}

	/// @notice Update reward variables for all pools. Be careful of gas spending!
	/// @param pids Pool IDs of all to be updated. Make sure to update all active pools.
	function massUpdatePools(uint256[] calldata pids) external {
		uint256 len = pids.length;
		for (uint256 i = 0; i < len; ++i) {
			updatePool(pids[i]);
		}
	}

	/// @notice Calculates and returns the `amount` of SUSHI per block.
	function sushiPerBlock() public view returns (uint256 amount) {
		amount =
			uint256(MASTERCHEF_SUSHI_PER_BLOCK).mul(MASTER_CHEF.poolInfo(MASTER_PID).allocPoint) /
			MASTER_CHEF.totalAllocPoint();
	}

	/// @notice Update reward variables of the given pool.
	/// @param pid The index of the pool. See `poolInfo`.
	/// @return pool Returns the pool that was updated.
	function updatePool(uint256 pid) public returns (PoolInfo memory pool) {
		pool = poolInfo[pid];
		if (block.number > pool.lastRewardBlock) {
			uint256 lpSupply = lpToken[pid].balanceOf(address(this));
			if (lpSupply > 0) {
				uint256 blocks = block.number.sub(pool.lastRewardBlock);
				uint256 sushiReward = blocks.mul(sushiPerBlock()).mul(pool.allocPoint) / totalAllocPoint;
				pool.accSushiPerShare = pool.accSushiPerShare.add(
					(sushiReward.mul(ACC_SUSHI_PRECISION) / lpSupply).to128()
				);
			}
			pool.lastRewardBlock = block.number.to64();
			poolInfo[pid] = pool;
			emit LogUpdatePool(pid, pool.lastRewardBlock, lpSupply, pool.accSushiPerShare);
		}
	}

	/// @notice Deposit LP tokens to MCV2 for SUSHI allocation.
	/// @param pid The index of the pool. See `poolInfo`.
	/// @param amount LP token amount to deposit.
	/// @param to The receiver of `amount` deposit benefit.
	function deposit(
		uint256 pid,
		uint256 amount,
		address to
	) public {
		PoolInfo memory pool = updatePool(pid);
		UserInfo storage user = userInfo[pid][to];

		// Effects
		user.amount = user.amount.add(amount);
		user.rewardDebt = user.rewardDebt.add(int256(amount.mul(pool.accSushiPerShare) / ACC_SUSHI_PRECISION));

		// Interactions
		IRewarder _rewarder = rewarder[pid];
		if (address(_rewarder) != address(0)) {
			_rewarder.onSushiReward(pid, to, to, 0, user.amount);
		}

		lpToken[pid].safeTransferFrom(msg.sender, address(this), amount);

		emit Deposit(msg.sender, pid, amount, to);
	}

	/// @notice Withdraw LP tokens from MCV2.
	/// @param pid The index of the pool. See `poolInfo`.
	/// @param amount LP token amount to withdraw.
	/// @param to Receiver of the LP tokens.
	function withdraw(
		uint256 pid,
		uint256 amount,
		address to
	) public {
		PoolInfo memory pool = updatePool(pid);
		UserInfo storage user = userInfo[pid][msg.sender];

		// Effects
		user.rewardDebt = user.rewardDebt.sub(int256(amount.mul(pool.accSushiPerShare) / ACC_SUSHI_PRECISION));
		user.amount = user.amount.sub(amount);

		// Interactions
		IRewarder _rewarder = rewarder[pid];
		if (address(_rewarder) != address(0)) {
			_rewarder.onSushiReward(pid, msg.sender, to, 0, user.amount);
		}

		lpToken[pid].safeTransfer(to, amount);

		emit Withdraw(msg.sender, pid, amount, to);
	}

	/// @notice Harvest proceeds for transaction sender to `to`.
	/// @param pid The index of the pool. See `poolInfo`.
	/// @param to Receiver of SUSHI rewards.
	function harvest(uint256 pid, address to) public {
		PoolInfo memory pool = updatePool(pid);
		UserInfo storage user = userInfo[pid][msg.sender];
		int256 accumulatedSushi = int256(user.amount.mul(pool.accSushiPerShare) / ACC_SUSHI_PRECISION);
		uint256 _pendingSushi = accumulatedSushi.sub(user.rewardDebt).toUInt256();

		// Effects
		user.rewardDebt = accumulatedSushi;

		// Interactions
		if (_pendingSushi != 0) {
			SUSHI.safeTransfer(to, _pendingSushi);
		}

		IRewarder _rewarder = rewarder[pid];
		if (address(_rewarder) != address(0)) {
			_rewarder.onSushiReward(pid, msg.sender, to, _pendingSushi, user.amount);
		}

		emit Harvest(msg.sender, pid, _pendingSushi);
	}

	/// @notice Withdraw LP tokens from MCV2 and harvest proceeds for transaction sender to `to`.
	/// @param pid The index of the pool. See `poolInfo`.
	/// @param amount LP token amount to withdraw.
	/// @param to Receiver of the LP tokens and SUSHI rewards.
	function withdrawAndHarvest(
		uint256 pid,
		uint256 amount,
		address to
	) public {
		PoolInfo memory pool = updatePool(pid);
		UserInfo storage user = userInfo[pid][msg.sender];
		int256 accumulatedSushi = int256(user.amount.mul(pool.accSushiPerShare) / ACC_SUSHI_PRECISION);
		uint256 _pendingSushi = accumulatedSushi.sub(user.rewardDebt).toUInt256();

		// Effects
		user.rewardDebt = accumulatedSushi.sub(int256(amount.mul(pool.accSushiPerShare) / ACC_SUSHI_PRECISION));
		user.amount = user.amount.sub(amount);

		// Interactions
		SUSHI.safeTransfer(to, _pendingSushi);

		IRewarder _rewarder = rewarder[pid];
		if (address(_rewarder) != address(0)) {
			_rewarder.onSushiReward(pid, msg.sender, to, _pendingSushi, user.amount);
		}

		lpToken[pid].safeTransfer(to, amount);

		emit Withdraw(msg.sender, pid, amount, to);
		emit Harvest(msg.sender, pid, _pendingSushi);
	}

	/// @notice Harvests SUSHI from `MASTER_CHEF` MCV1 and pool `MASTER_PID` to this MCV2 contract.
	function harvestFromMasterChef() public {
		MASTER_CHEF.deposit(MASTER_PID, 0);
	}

	/// @notice Withdraw without caring about rewards. EMERGENCY ONLY.
	/// @param pid The index of the pool. See `poolInfo`.
	/// @param to Receiver of the LP tokens.
	function emergencyWithdraw(uint256 pid, address to) public {
		UserInfo storage user = userInfo[pid][msg.sender];
		uint256 amount = user.amount;
		user.amount = 0;
		user.rewardDebt = 0;

		IRewarder _rewarder = rewarder[pid];
		if (address(_rewarder) != address(0)) {
			_rewarder.onSushiReward(pid, msg.sender, to, 0, 0);
		}

		// Note: transfer can fail or succeed if `amount` is zero.
		lpToken[pid].safeTransfer(to, amount);
		emit EmergencyWithdraw(msg.sender, pid, amount, to);
	}
}

// File: @boringcrypto/boring-solidity/contracts/interfaces/IERC20.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    // EIP 2612
    function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
}
// File: @boringcrypto/boring-solidity/contracts/BoringBatchable.sol



// SPDX-License-Identifier: UNLICENSED
// Audit on 5-Jan-2021 by Keno and BoringCrypto

// P1 - P3: OK
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
// solhint-disable avoid-low-level-calls

import "./libraries/BoringERC20.sol";

// T1 - T4: OK
contract BaseBoringBatchable {
    function _getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
        // If the _res length is less than 68, then the transaction failed silently (without a revert message)
        if (_returnData.length < 68) return "Transaction reverted silently";

        assembly {
            // Slice the sighash.
            _returnData := add(_returnData, 0x04)
        }
        return abi.decode(_returnData, (string)); // All that remains is the revert string
    }    
    
    // F3 - F9: OK
    // F1: External is ok here because this is the batch function, adding it to a batch makes no sense
    // F2: Calls in the batch may be payable, delegatecall operates in the same context, so each call in the batch has access to msg.value
    // C1 - C21: OK
    // C3: The length of the loop is fully under user control, so can't be exploited
    // C7: Delegatecall is only used on the same contract, so it's safe
    function batch(bytes[] calldata calls, bool revertOnFail) external payable returns(bool[] memory successes, bytes[] memory results) {
        // Interactions
        successes = new bool[](calls.length);
        results = new bytes[](calls.length);
        for (uint256 i = 0; i < calls.length; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(calls[i]);
            require(success || !revertOnFail, _getRevertMsg(result));
            successes[i] = success;
            results[i] = result;
        }
    }
}

// T1 - T4: OK
contract BoringBatchable is BaseBoringBatchable {
    // F1 - F9: OK
    // F6: Parameters can be used front-run the permit and the user's permit will fail (due to nonce or other revert)
    //     if part of a batch this could be used to grief once as the second call would not need the permit
    // C1 - C21: OK
    function permitToken(IERC20 token, address from, address to, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
        // Interactions
        // X1 - X5
        token.permit(from, to, amount, deadline, v, r, s);
    }
}
// File: contracts/libraries/math/SignedSafeMath.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;

library SignedSafeMath {
	int256 private constant _INT256_MIN = -2**255;

	/**
	 * @dev Returns the multiplication of two signed integers, reverting on
	 * overflow.
	 *
	 * Counterpart to Solidity's `*` operator.
	 *
	 * Requirements:
	 *
	 * - Multiplication cannot overflow.
	 */
	function mul(int256 a, int256 b) internal pure returns (int256) {
		// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
		// benefit is lost if 'b' is also tested.
		// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
		if (a == 0) {
			return 0;
		}

		require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow");

		int256 c = a * b;
		require(c / a == b, "SignedSafeMath: multiplication overflow");

		return c;
	}

	/**
	 * @dev Returns the integer division of two signed integers. Reverts on
	 * division by zero. The result is rounded towards zero.
	 *
	 * Counterpart to Solidity's `/` operator. Note: this function uses a
	 * `revert` opcode (which leaves remaining gas untouched) while Solidity
	 * uses an invalid opcode to revert (consuming all remaining gas).
	 *
	 * Requirements:
	 *
	 * - The divisor cannot be zero.
	 */
	function div(int256 a, int256 b) internal pure returns (int256) {
		require(b != 0, "SignedSafeMath: division by zero");
		require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow");

		int256 c = a / b;

		return c;
	}

	/**
	 * @dev Returns the subtraction of two signed integers, reverting on
	 * overflow.
	 *
	 * Counterpart to Solidity's `-` operator.
	 *
	 * Requirements:
	 *
	 * - Subtraction cannot overflow.
	 */
	function sub(int256 a, int256 b) internal pure returns (int256) {
		int256 c = a - b;
		require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");

		return c;
	}

	/**
	 * @dev Returns the addition of two signed integers, reverting on
	 * overflow.
	 *
	 * Counterpart to Solidity's `+` operator.
	 *
	 * Requirements:
	 *
	 * - Addition cannot overflow.
	 */
	function add(int256 a, int256 b) internal pure returns (int256) {
		int256 c = a + b;
		require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");

		return c;
	}

	function toUInt256(int256 a) internal pure returns (uint256) {
		require(a >= 0, "Integer < 0");
		return uint256(a);
	}
}

// File: contracts/interfaces/sushi/IMasterChef.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import { BoringERC20, IERC20 } from "@boringcrypto/boring-solidity/contracts/libraries/BoringERC20.sol";

interface IMasterChef {
	using BoringERC20 for IERC20;
	struct UserInfo {
		uint256 amount; // How many LP tokens the user has provided.
		uint256 rewardDebt; // Reward debt. See explanation below.
	}

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

	function poolInfo(uint256 pid) external view returns (IMasterChef.PoolInfo memory);

	function totalAllocPoint() external view returns (uint256);

	function deposit(uint256 _pid, uint256 _amount) external;
}