// Sources flattened with hardhat v2.0.11 https://hardhat.org
// File @boringcrypto/boring-solidity/contracts/interfaces/IERC20.sol@v1.2.2
// 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);
/// @notice 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.2.2
pragma solidity 0.6.12;
// solhint-disable avoid-low-level-calls
library BoringERC20 {
bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol()
bytes4 private constant SIG_NAME = 0x06fdde03; // name()
bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals()
bytes4 private constant SIG_TRANSFER = 0xa9059cbb; // transfer(address,uint256)
bytes4 private constant SIG_TRANSFER_FROM = 0x23b872dd; // transferFrom(address,address,uint256)
function returnDataToString(bytes memory data) internal pure returns (string memory) {
if (data.length >= 64) {
return abi.decode(data, (string));
} else if (data.length == 32) {
uint8 i = 0;
while(i < 32 && data[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && data[i] != 0; i++) {
bytesArray[i] = data[i];
}
return string(bytesArray);
} else {
return "???";
}
}
/// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string.
/// @param token The address of the ERC-20 token contract.
/// @return (string) Token symbol.
function safeSymbol(IERC20 token) internal view returns (string memory) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_SYMBOL));
return success ? returnDataToString(data) : "???";
}
/// @notice Provides a safe ERC20.name version which returns '???' as fallback string.
/// @param token The address of the ERC-20 token contract.
/// @return (string) Token name.
function safeName(IERC20 token) internal view returns (string memory) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_NAME));
return success ? returnDataToString(data) : "???";
}
/// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value.
/// @param token The address of the ERC-20 token contract.
/// @return (uint8) Token decimals.
function safeDecimals(IERC20 token) internal view returns (uint8) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_DECIMALS));
return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
}
/// @notice Provides a safe ERC20.transfer version for different ERC-20 implementations.
/// Reverts on a failed transfer.
/// @param token The address of the ERC-20 token.
/// @param to Transfer tokens to.
/// @param amount The token amount.
function safeTransfer(
IERC20 token,
address to,
uint256 amount
) internal {
(bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_TRANSFER, to, amount));
require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: Transfer failed");
}
/// @notice Provides a safe ERC20.transferFrom version for different ERC-20 implementations.
/// Reverts on a failed transfer.
/// @param token The address of the ERC-20 token.
/// @param from Transfer tokens from.
/// @param to Transfer tokens to.
/// @param amount The token amount.
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
(bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_TRANSFER_FROM, 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.2.2
pragma solidity 0.6.12;
/// @notice A library for performing overflow-/underflow-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);
}
}
/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint128.
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");
}
}
/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint64.
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");
}
}
/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint32.
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.2.2
pragma solidity 0.6.12;
// Audit on 5-Jan-2021 by Keno and BoringCrypto
// Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol + Claimable.sol
// Edited by BoringCrypto
contract BoringOwnableData {
address public owner;
address public pendingOwner;
}
contract BoringOwnable is BoringOwnableData {
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @notice `owner` defaults to msg.sender on construction.
constructor() public {
owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.
/// Can only be invoked by the current `owner`.
/// @param newOwner Address of the new owner.
/// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.
/// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.
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;
}
}
/// @notice Needs to be called by `pendingOwner` to claim ownership.
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);
}
/// @notice Only allows the `owner` to execute the function.
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 TruRewarder is IRewarder, BoringOwnable{
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;
}
/// @notice Info of each 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;
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 (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");
emit LogInit();
}
function onSushiReward (uint256 pid, address _user, address to, uint256, uint256 lpToken) onlyMCV2 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.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);
}
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 accSushiPerShare = pool.accSushiPerShare;
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);
accSushiPerShare = accSushiPerShare.add(sushiReward.mul(ACC_TOKEN_PRECISION) / lpSupply);
}
pending = (user.amount.mul(accSushiPerShare) / ACC_TOKEN_PRECISION).sub(user.rewardDebt);
}
/// @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.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);
}
}
}
{
"compilationTarget": {
"TruRewarder.sol": "TruRewarder"
},
"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":[],"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":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"allocPoint","type":"uint256"}],"name":"LogPoolAddition","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":"uint256","name":"allocPoint","type":"uint256"}],"name":"LogSetPool","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":"accSushiPerShare","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":"lpToken","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":"accSushiPerShare","type":"uint128"},{"internalType":"uint64","name":"lastRewardTime","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerSecond","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":"accSushiPerShare","type":"uint128"},{"internalType":"uint64","name":"lastRewardTime","type":"uint64"}],"internalType":"struct TruRewarder.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"}],"stateMutability":"view","type":"function"}]