Skip to content

Commit

Permalink
Added complex rewarder based on MasterChef architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
Clearwood committed Apr 15, 2021
1 parent 52c646e commit b332324
Showing 1 changed file with 173 additions and 0 deletions.
173 changes: 173 additions & 0 deletions contracts/mocks/ComplexRewarder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// SPDX-License-Identifier: MIT

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

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

IERC20 private immutable rewardToken;

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

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

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

uint256[] public poolIds;

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

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

address private immutable MASTERCHEF_V2;

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

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


function onSushiReward (uint256 pid, address _user, address to, uint256, uint256 lpToken) onlyMCV2 override external {
PoolInfo memory pool = updatePool(pid);
UserInfo storage user = userInfo[pid][_user];
uint256 pending;
if (user.amount > 0) {
pending =
(user.amount.mul(pool.accSushiPerShare) / ACC_TOKEN_PRECISION).sub(
user.rewardDebt
);
rewardToken.safeTransfer(to, pending);
}
user.amount = lpToken;
user.rewardDebt = lpToken.mul(pool.accSushiPerShare) / ACC_TOKEN_PRECISION;
emit LogOnReward(_user, pid, pending, to);
}

function pendingTokens(uint256 pid, address user, uint256) override external returns (IERC20[] memory rewardTokens, uint256[] memory rewardAmounts) {
IERC20[] memory _rewardTokens = new IERC20[](1);
_rewardTokens[0] = (rewardToken);
uint256[] memory _rewardAmounts = new uint256[](1);
_rewardAmounts[0] = pendingToken(pid, user);
return (_rewardTokens, _rewardAmounts);
}

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

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

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

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

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

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

/// @notice Update reward variables for all pools. Be careful of gas spending!
/// @param pids Pool IDs of all to be updated. Make sure to update all active pools.
function massUpdatePools(uint256[] calldata pids) 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.number > pool.lastRewardBlock) {
uint256 lpSupply = MasterChefV2(MASTERCHEF_V2).lpToken(pid).balanceOf(MASTERCHEF_V2);

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

}

0 comments on commit b332324

Please sign in to comment.