Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
//SPDX-License-Identifier: MIT
pragma solidity >=0.6.11 <=0.6.12;
interface IAddressRegistry { }
contract BaseController {
address public immutable manager;
IAddressRegistry public immutable addressRegistry;
constructor(address _manager, address _addressRegistry) public {
require(_manager != address(0), "INVALID_ADDRESS");
require(_addressRegistry != address(0), "INVALID_ADDRESS");
manager = _manager;
addressRegistry = IAddressRegistry(_addressRegistry);
}
modifier onlyManager() {
require(msg.sender == manager, "NOT_MANAGER_ADDRESS");
_;
}
}
// Sources flattened with hardhat v2.0.11 https://hardhat.org
// File @boringcrypto/boring-solidity/contracts/libraries/[email protected]
// 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/interfaces/[email protected]
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);
function transferFrom(address owner, 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/libraries/[email protected]
pragma solidity 0.6.12;
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/[email protected]
// Audit on 5-Jan-2021 by Keno and BoringCrypto
// P1 - P3: OK
pragma solidity 0.6.12;
// solhint-disable avoid-low-level-calls
// 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 @boringcrypto/boring-solidity/contracts/[email protected]
// 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/libraries/SignedSafeMath.sol
pragma solidity 0.6.12;
library SignedSafeMath {
int256 constant private _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/IRewarder.sol
pragma solidity 0.6.12;
interface IRewarder {
using BoringERC20 for IERC20;
function rewardToken() external returns(address);
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 contracts/interfaces/IMasterChef.sol
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
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;
}
// File contracts/Masterchef.sol
pragma solidity 0.6.12;
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
/// @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 MasterChef is BoringOwnable, BoringBatchable, ReentrancyGuard {
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 lastRewardTime;
uint64 allocPoint;
IRewarder[] rewarders;
}
/// @notice Address of SUSHI contract.
IERC20 public immutable SUSHI;
/// @notice Info of each MCV2 pool.
PoolInfo[] public poolInfo;
/// @notice Address of the LP token for each MCV2 pool.
IERC20[] public lpToken;
/// @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 public sushiPerSecond;
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);
event LogSetPool(uint256 indexed pid, uint256 allocPoint);
event LogUpdatePool(uint256 indexed pid, uint64 lastRewardTime, uint256 lpSupply, uint256 accSushiPerShare);
event LogSushiPerSecond(uint256 sushiPerSecond);
event LogRewarderAdded(uint256 indexed pid, IRewarder indexed rewarder);
/// @param _sushi The SUSHI token contract address.
constructor(IERC20 _sushi) public {
SUSHI = _sushi;
}
/// @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.
function add(uint256 allocPoint, IERC20 _lpToken) public onlyOwner {
totalAllocPoint = totalAllocPoint.add(allocPoint);
lpToken.push(_lpToken);
poolInfo.push(PoolInfo({
allocPoint: allocPoint.to64(),
lastRewardTime: block.timestamp.to64(),
accSushiPerShare: 0,
rewarders: (new IRewarder[](0))
}));
emit LogPoolAddition(lpToken.length.sub(1), allocPoint, _lpToken);
}
/// @notice Add a new Rewarder instance to a given pool
/// @param _pid The index of the pool. See `poolInfo`.
/// @param _rewarder Address of the rewarder delegate.
function addRewarder(uint256 _pid, IRewarder _rewarder) public onlyOwner {
for( uint256 i = 0; i < poolInfo[_pid].rewarders.length; i++ ) {
require(address(poolInfo[_pid].rewarders[i]) != address(_rewarder), "already added");
}
poolInfo[_pid].rewarders.push(_rewarder);
emit LogRewarderAdded(_pid, _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.
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 Sets the sushi per second to be distributed. Can only be called by the owner.
/// @param _sushiPerSecond The amount of Sushi to be distributed per second.
function setSushiPerSecond(uint256 _sushiPerSecond) public onlyOwner {
sushiPerSecond = _sushiPerSecond;
emit LogSushiPerSecond(_sushiPerSecond);
}
/// @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.timestamp > pool.lastRewardTime && lpSupply != 0) {
uint256 time = block.timestamp.sub(pool.lastRewardTime);
uint256 sushiReward = time.mul(sushiPerSecond).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 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.timestamp > pool.lastRewardTime) {
uint256 lpSupply = lpToken[pid].balanceOf(address(this));
if (lpSupply > 0) {
uint256 time = block.timestamp.sub(pool.lastRewardTime);
uint256 sushiReward = time.mul(sushiPerSecond).mul(pool.allocPoint) / totalAllocPoint;
pool.accSushiPerShare = pool.accSushiPerShare.add((sushiReward.mul(ACC_SUSHI_PRECISION) / lpSupply).to128());
}
pool.lastRewardTime = block.timestamp.to64();
poolInfo[pid] = pool;
emit LogUpdatePool(pid, pool.lastRewardTime, 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) nonReentrant 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
for( uint256 i = 0; i < poolInfo[pid].rewarders.length; i++) {
IRewarder _rewarder = poolInfo[pid].rewarders[i];
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) nonReentrant 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
for( uint256 i = 0; i < poolInfo[pid].rewarders.length; i++) {
IRewarder _rewarder = poolInfo[pid].rewarders[i];
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) nonReentrant 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);
}
for( uint256 i = 0; i < poolInfo[pid].rewarders.length; i++) {
IRewarder _rewarder = poolInfo[pid].rewarders[i];
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) nonReentrant 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);
for( uint256 i = 0; i < poolInfo[pid].rewarders.length; i++) {
IRewarder _rewarder = poolInfo[pid].rewarders[i];
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 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) nonReentrant public {
UserInfo storage user = userInfo[pid][msg.sender];
uint256 amount = user.amount;
user.amount = 0;
user.rewardDebt = 0;
for( uint256 i = 0; i < poolInfo[pid].rewarders.length; i++) {
IRewarder _rewarder = poolInfo[pid].rewarders[i];
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);
}
function getRewarder(uint256 _pid, uint256 _rid) public view returns(address) {
return address(poolInfo[_pid].rewarders[_rid]);
}
}
// Sources flattened with hardhat v2.0.11 https://hardhat.org
// File @boringcrypto/boring-solidity/contracts/interfaces/[email protected]
// 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/libraries/[email protected]
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 contracts/interfaces/IRewarder.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/[email protected]
// 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/[email protected]
// 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 @boringcrypto/boring-solidity/contracts/[email protected]
// 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
// 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/SignedSafeMath.sol
pragma solidity 0.6.12;
library SignedSafeMath {
int256 constant private _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/IMasterChef.sol
pragma solidity 0.6.12;
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;
}
// File contracts/MasterChefV2.sol
pragma solidity 0.6.12;
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, BoringBatchable {
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 public constant MASTERCHEF_SUSHI_PER_BLOCK = 1e20;
uint256 public 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 contracts/mocks/ComplexRewarderTime.sol
pragma solidity 0.6.12;
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
/// @author @0xKeno
contract Rewarder is IRewarder, BoringOwnable, ReentrancyGuard{
using BoringMath for uint256;
using BoringMath128 for uint128;
using BoringERC20 for IERC20;
IERC20 public 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 lastRewardTime;
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 public totalAllocPoint;
uint256 public rewardPerSecond;
uint256 public constant ACC_TOKEN_PRECISION = 1e12;
address public immutable MASTERCHEF_V2;
modifier onlyMCV2 {
require(
msg.sender == MASTERCHEF_V2,
"Only MCV2 can call this function."
);
_;
}
address public funder;
modifier onlyFunder() {
require(msg.sender == funder, "Ownable: caller is not the funder");
_;
}
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 lastRewardTime, uint256 lpSupply, uint256 accSushiPerShare);
event LogRewardPerSecond(uint256 rewardPerSecond);
event LogInit();
constructor (IERC20 _rewardToken, uint256 _rewardPerSecond, address _MASTERCHEF_V2) public {
rewardToken = _rewardToken;
rewardPerSecond = _rewardPerSecond;
MASTERCHEF_V2 = _MASTERCHEF_V2;
funder = msg.sender;
}
function onSushiReward (uint256 pid, address _user, address to, uint256, uint256 lpToken) onlyMCV2 nonReentrant 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);
}
/// @notice Sets the sushi per second to be distributed. Can only be called by the owner.
/// @param _rewardPerSecond The amount of Sushi to be distributed per second.
function setRewardPerSecond(uint256 _rewardPerSecond) public onlyOwner {
rewardPerSecond = _rewardPerSecond;
emit LogRewardPerSecond(_rewardPerSecond);
}
/// @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].lastRewardTime == 0, "Pool already exists");
uint256 lastRewardTime = block.timestamp;
totalAllocPoint = totalAllocPoint.add(allocPoint);
poolInfo[_pid] = PoolInfo({
allocPoint: allocPoint.to64(),
lastRewardTime: lastRewardTime.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.timestamp > pool.lastRewardTime && lpSupply != 0) {
uint256 time = block.timestamp.sub(pool.lastRewardTime);
uint256 sushiReward = time.mul(rewardPerSecond).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) external {
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];
if (block.timestamp > pool.lastRewardTime) {
uint256 lpSupply = MasterChefV2(MASTERCHEF_V2).lpToken(pid).balanceOf(MASTERCHEF_V2);
if (lpSupply > 0) {
uint256 time = block.timestamp.sub(pool.lastRewardTime);
uint256 sushiReward = time.mul(rewardPerSecond).mul(pool.allocPoint) / totalAllocPoint;
pool.accSushiPerShare = pool.accSushiPerShare.add((sushiReward.mul(ACC_TOKEN_PRECISION) / lpSupply).to128());
}
pool.lastRewardTime = block.timestamp.to64();
poolInfo[pid] = pool;
emit LogUpdatePool(pid, pool.lastRewardTime, lpSupply, pool.accSushiPerShare);
}
}
function transferFunder(address newFunder) public onlyFunder {
funder = newFunder;
}
function reclaimTokens(uint256 amount, address payable to) public onlyFunder {
rewardToken.safeTransfer(to, amount);
}
}