forked from sushi-labs/sushiswap
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added complex rewarder based on MasterChef architecture
- Loading branch information
Showing
1 changed file
with
173 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
|
||
} |