// File @boringcrypto/boring-solidity/contracts/interfaces/IERC20.sol@v1.0.4
// 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/BoringERC20.sol@v1.0.4
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 contracts/interfaces/IRewarder.sol
pragma solidity 0.6.12;
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/BoringMath.sol@v1.0.4
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@v1.0.4
// 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/mocks/CloneRewarderTime.sol
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
interface IMasterChefV2 {
function lpToken(uint256 pid) external view returns (IERC20 _lpToken);
}
/// @author @0xKeno
contract GasRewarder is IRewarder, BoringOwnable{
using BoringMath for uint256;
using BoringMath128 for uint128;
using BoringERC20 for IERC20;
IERC20 public rewardToken;
/// @notice Info of each Rewarder user.
/// `amount` LP token amount the user has provided.
/// `rewardDebt` The amount of Reward Token entitled to the user.
struct UserInfo {
uint256 amount;
uint256 rewardDebt;
uint256 unpaidRewards;
}
/// @notice Info of the rewarder pool
struct PoolInfo {
uint128 accToken1PerShare;
uint64 lastRewardTime;
}
/// @notice Mapping to track the rewarder pool.
mapping (uint256 => PoolInfo) public poolInfo;
/// @notice Info of each user that stakes LP tokens.
mapping (uint256 => mapping (address => UserInfo)) public userInfo;
uint256 public rewardPerSecond;
IERC20 public masterLpToken;
uint256 private constant ACC_TOKEN_PRECISION = 1e12;
address public immutable MASTERCHEF_V2;
uint256 internal unlocked;
modifier lock() {
require(unlocked == 1, "LOCKED");
unlocked = 2;
_;
unlocked = 1;
}
event LogOnReward(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);
event LogUpdatePool(uint256 indexed pid, uint64 lastRewardTime, uint256 lpSupply, uint256 accToken1PerShare);
event LogRewardPerSecond(uint256 rewardPerSecond);
event LogInit(IERC20 indexed rewardToken, address owner, uint256 rewardPerSecond, IERC20 indexed masterLpToken);
constructor (address _MASTERCHEF_V2) public {
MASTERCHEF_V2 = _MASTERCHEF_V2;
}
/// @notice Serves as the constructor for clones, as clones can't have a regular constructor
/// @dev `data` is abi encoded in the format: (IERC20 collateral, IERC20 asset, IOracle oracle, bytes oracleData)
function init(bytes calldata data) public payable {
require(rewardToken == IERC20(0), "Rewarder: already initialized");
(rewardToken, owner, rewardPerSecond, masterLpToken) = abi.decode(data, (IERC20, address, uint256, IERC20));
require(rewardToken != IERC20(0), "Rewarder: bad token");
unlocked = 1;
emit LogInit(rewardToken, owner, rewardPerSecond, masterLpToken);
}
function onSushiReward (uint256 pid, address _user, address to, uint256, uint256 lpTokenAmount) onlyMCV2 lock override external {
require(IMasterChefV2(MASTERCHEF_V2).lpToken(pid) == masterLpToken);
PoolInfo memory pool = updatePool(pid);
UserInfo storage user = userInfo[pid][_user];
uint256 pending;
if (user.amount > 0) {
pending =
(user.amount.mul(pool.accToken1PerShare) / ACC_TOKEN_PRECISION).sub(
user.rewardDebt
).add(user.unpaidRewards);
uint256 balance = rewardToken.balanceOf(address(this));
if (pending > balance) {
rewardToken.safeTransfer(to, balance);
user.unpaidRewards = pending - balance;
} else {
rewardToken.safeTransfer(to, pending);
user.unpaidRewards = 0;
}
}
user.amount = lpTokenAmount;
user.rewardDebt = lpTokenAmount.mul(pool.accToken1PerShare) / ACC_TOKEN_PRECISION;
emit LogOnReward(_user, pid, pending - user.unpaidRewards, 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);
}
function rewardRates() external view returns (uint256[] memory) {
uint256[] memory _rewardRates = new uint256[](1);
_rewardRates[0] = rewardPerSecond;
return (_rewardRates);
}
/// @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 Allows owner to reclaim/withdraw any tokens (including reward tokens) held by this contract
/// @param token Token to reclaim, use 0x00 for Ethereum
/// @param amount Amount of tokens to reclaim
/// @param to Receiver of the tokens, first of his name, rightful heir to the lost tokens,
/// reightful owner of the extra tokens, and ether, protector of mistaken transfers, mother of token reclaimers,
/// the Khaleesi of the Great Token Sea, the Unburnt, the Breaker of blockchains.
function reclaimTokens(address token, uint256 amount, address payable to) public onlyOwner {
if (token == address(0)) {
to.transfer(amount);
} else {
IERC20(token).safeTransfer(to, amount);
}
}
modifier onlyMCV2 {
require(
msg.sender == MASTERCHEF_V2,
"Only MCV2 can call this function."
);
_;
}
/// @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 accToken1PerShare = pool.accToken1PerShare;
uint256 lpSupply = IMasterChefV2(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);
accToken1PerShare = accToken1PerShare.add(sushiReward.mul(ACC_TOKEN_PRECISION) / lpSupply);
}
pending = (user.amount.mul(accToken1PerShare) / ACC_TOKEN_PRECISION).sub(user.rewardDebt).add(user.unpaidRewards);
}
/// @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 = IMasterChefV2(MASTERCHEF_V2).lpToken(pid).balanceOf(MASTERCHEF_V2);
if (lpSupply > 0) {
uint256 time = block.timestamp.sub(pool.lastRewardTime);
uint256 sushiReward = time.mul(rewardPerSecond);
pool.accToken1PerShare = pool.accToken1PerShare.add((sushiReward.mul(ACC_TOKEN_PRECISION) / lpSupply).to128());
}
pool.lastRewardTime = block.timestamp.to64();
poolInfo[pid] = pool;
emit LogUpdatePool(pid, pool.lastRewardTime, lpSupply, pool.accToken1PerShare);
}
}
}
{
"compilationTarget": {
"contracts/CloneRewarderTime.sol": "GasRewarder"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_MASTERCHEF_V2","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardPerSecond","type":"uint256"},{"indexed":true,"internalType":"contract IERC20","name":"masterLpToken","type":"address"}],"name":"LogInit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"LogOnReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rewardPerSecond","type":"uint256"}],"name":"LogRewardPerSecond","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"lastRewardTime","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"lpSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accToken1PerShare","type":"uint256"}],"name":"LogUpdatePool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"MASTERCHEF_V2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"init","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"masterLpToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pid","type":"uint256"},{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"lpTokenAmount","type":"uint256"}],"name":"onSushiReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"pendingToken","outputs":[{"internalType":"uint256","name":"pending","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pid","type":"uint256"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"pendingTokens","outputs":[{"internalType":"contract IERC20[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"rewardAmounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolInfo","outputs":[{"internalType":"uint128","name":"accToken1PerShare","type":"uint128"},{"internalType":"uint64","name":"lastRewardTime","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"to","type":"address"}],"name":"reclaimTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardPerSecond","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardRates","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_rewardPerSecond","type":"uint256"}],"name":"setRewardPerSecond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"},{"internalType":"bool","name":"direct","type":"bool"},{"internalType":"bool","name":"renounce","type":"bool"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pid","type":"uint256"}],"name":"updatePool","outputs":[{"components":[{"internalType":"uint128","name":"accToken1PerShare","type":"uint128"},{"internalType":"uint64","name":"lastRewardTime","type":"uint64"}],"internalType":"struct GasRewarder.PoolInfo","name":"pool","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rewardDebt","type":"uint256"},{"internalType":"uint256","name":"unpaidRewards","type":"uint256"}],"stateMutability":"view","type":"function"}]