From a20bd58c87840c846ba717e82b17b0d6476b064f Mon Sep 17 00:00:00 2001 From: mul53 Date: Fri, 25 Nov 2022 19:48:57 +0200 Subject: [PATCH 1/3] add PoolRewardDistributor --- contracts/PoolRewardDistributor.sol | 59 ++++++++++++++++++++++++++++ contracts/interfaces/IMasterChef.sol | 12 ++++++ 2 files changed, 71 insertions(+) create mode 100644 contracts/PoolRewardDistributor.sol diff --git a/contracts/PoolRewardDistributor.sol b/contracts/PoolRewardDistributor.sol new file mode 100644 index 0000000..8e3ec7d --- /dev/null +++ b/contracts/PoolRewardDistributor.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import "@openzeppelin/contracts/math/SafeMath.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import './uniswap/interfaces/IWETH.sol'; +import './interfaces/IMasterChef.sol'; +import "./boringcrypto/BoringOwnable.sol"; + +/** + * @title PoolRewardDistributor + * @notice Distribute rewards to pools + */ +contract PoolRewardDistributor is BoringOwnable { + using SafeMath for uint256; + using SafeERC20 for IERC20; + + address public immutable masterChef; + address public immutable WETH; + + constructor(address _masterChef, address _WETH) public { + masterChef = _masterChef; + WETH = _WETH; + } + + function recoverFuse() public onlyOwner { + uint256 fuseBalance = address(this).balance; + address payable _owner = address(this); + _owner.call{value: fuseBalance}(""); + } + + function recoverToken(address token) public onlyOwner { + uint256 tokenBalance = IERC20(token).balanceOf(address(this)); + IERC20(token).transfer(owner, tokenBalance); + } + + function _distributeRewards(uint256 rewardAmount) internal { + uint256 pools = IMasterChef(masterChef).poolLength(); + uint256 totalAllocPoints = IMasterChef(masterChef).totalAllocPoint(); + + for (uint256 i = 0; i < pools; i++) { + IMasterChef.PoolInfo memory pool = IMasterChef(masterChef).poolInfo(i); + uint256 amount = (pool.allocPoint.div(totalAllocPoints)).mul(rewardAmount); + + if (address(pool.rewarder) != address(0) && amount > 0) { + IWETH(WETH).transfer(address(pool.rewarder), amount); + } + } + } + + receive() external payable { + require(msg.sender == owner); + + IWETH(WETH).deposit{value: msg.value}(); + _distributeRewards(msg.value); + } +} diff --git a/contracts/interfaces/IMasterChef.sol b/contracts/interfaces/IMasterChef.sol index 3e194b3..d8c1811 100644 --- a/contracts/interfaces/IMasterChef.sol +++ b/contracts/interfaces/IMasterChef.sol @@ -1,8 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; + import "../libraries/BoringERC20.sol"; +interface IRewarder { + function onVoltReward(address user, uint256 newLpAmount) external; + + function pendingTokens(address user) external view returns (uint256 pending); + + function rewardToken() external view returns (IERC20); +} + interface IMasterChef { using BoringERC20 for IERC20; struct UserInfo { @@ -15,6 +24,7 @@ interface IMasterChef { uint256 allocPoint; // How many allocation points assigned to this pool. JOE to distribute per block. uint256 lastRewardTimestamp; // Last block number that JOE distribution occurs. uint256 accJoePerShare; // Accumulated JOE per share, times 1e12. See below. + IRewarder rewarder; } function poolInfo(uint256 pid) external view returns (IMasterChef.PoolInfo memory); @@ -28,4 +38,6 @@ interface IMasterChef { function treasuryPercent() external view returns (uint256); function investorPercent() external view returns (uint256); + + function poolLength() external view returns (uint256); } From 7a20a03973a406fa31c1e4da5104c3254988665d Mon Sep 17 00:00:00 2001 From: mul53 Date: Mon, 5 Dec 2022 19:58:52 +0200 Subject: [PATCH 2/3] add new methods --- contracts/PoolRewardDistributor.sol | 81 ++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 14 deletions(-) diff --git a/contracts/PoolRewardDistributor.sol b/contracts/PoolRewardDistributor.sol index 8e3ec7d..7fd6632 100644 --- a/contracts/PoolRewardDistributor.sol +++ b/contracts/PoolRewardDistributor.sol @@ -17,9 +17,18 @@ contract PoolRewardDistributor is BoringOwnable { using SafeMath for uint256; using SafeERC20 for IERC20; + + struct Pool { + uint256 pid; // id of pool + uint256 rewardAmount; // amount of rewards to distribue + } + address public immutable masterChef; address public immutable WETH; + uint256 public totalRewardAmount; + Pool[] public pools; + constructor(address _masterChef, address _WETH) public { masterChef = _masterChef; WETH = _WETH; @@ -27,8 +36,8 @@ contract PoolRewardDistributor is BoringOwnable { function recoverFuse() public onlyOwner { uint256 fuseBalance = address(this).balance; - address payable _owner = address(this); - _owner.call{value: fuseBalance}(""); + (bool sent,) = payable(owner).call{value: fuseBalance}(""); + require(sent); } function recoverToken(address token) public onlyOwner { @@ -36,24 +45,68 @@ contract PoolRewardDistributor is BoringOwnable { IERC20(token).transfer(owner, tokenBalance); } - function _distributeRewards(uint256 rewardAmount) internal { - uint256 pools = IMasterChef(masterChef).poolLength(); - uint256 totalAllocPoints = IMasterChef(masterChef).totalAllocPoint(); + function addPools(Pool[] memory _pools) external onlyOwner { + for (uint256 i = 0; i < _pools.length; i++) { + _addPool(_pools[i]); + } + } + + function addPool(Pool memory _pool) external onlyOwner { + _addPool(_pool); + } - for (uint256 i = 0; i < pools; i++) { - IMasterChef.PoolInfo memory pool = IMasterChef(masterChef).poolInfo(i); - uint256 amount = (pool.allocPoint.div(totalAllocPoints)).mul(rewardAmount); + function removePool(uint256 _pid) external onlyOwner { + _removePool(_pid); + } + + function distributeRewards() external payable onlyOwner { + for (uint256 i = 0; i < pools.length; i++) { + Pool memory pool = pools[i]; + IMasterChef.PoolInfo memory poolInfo = IMasterChef(masterChef).poolInfo(pool.pid); - if (address(pool.rewarder) != address(0) && amount > 0) { - IWETH(WETH).transfer(address(pool.rewarder), amount); + if (address(poolInfo.rewarder) != address(0)) { + IWETH(WETH).transfer(address(poolInfo.rewarder), pool.rewardAmount); } } } - receive() external payable { - require(msg.sender == owner); + function _addPool(Pool memory _pool) internal { + pools.push(_pool); + totalRewardAmount = totalRewardAmount.add(_pool.rewardAmount); + } + + function _removePool(uint256 _pid) internal { + require(_hasPool(_pid), 'pool not found'); + uint256 index = _poolIndex(_pid); + + Pool memory pool = pools[index]; + totalRewardAmount = totalRewardAmount.sub(pool.rewardAmount); + + Pool[] storage poolsStorage = pools; + + for (uint256 i = index; i < poolsStorage.length - 1; i++) { + poolsStorage[i] = poolsStorage[i + 1]; + } + + poolsStorage.pop(); + } + + function _poolIndex(uint256 _pid) internal view returns (uint256) { + require(_hasPool(_pid), 'pool not found'); + + for (uint256 i = 0; i < pools.length; i++) { + if (pools[i].pid == _pid) { + return i; + } + } + } - IWETH(WETH).deposit{value: msg.value}(); - _distributeRewards(msg.value); + function _hasPool(uint256 _pid) internal view returns (bool) { + for (uint256 i = 0; i < pools.length; i++) { + if (pools[i].pid == _pid) { + return true; + } + } + return false; } } From d37915df965f71b1e8f06e634776378239bbff88 Mon Sep 17 00:00:00 2001 From: mul53 Date: Wed, 7 Dec 2022 12:23:43 +0200 Subject: [PATCH 3/3] add fixes --- contracts/PoolRewardDistributor.sol | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/contracts/PoolRewardDistributor.sol b/contracts/PoolRewardDistributor.sol index 7fd6632..16a5100 100644 --- a/contracts/PoolRewardDistributor.sol +++ b/contracts/PoolRewardDistributor.sol @@ -60,6 +60,8 @@ contract PoolRewardDistributor is BoringOwnable { } function distributeRewards() external payable onlyOwner { + IWETH(WETH).deposit{value: msg.value}(); + for (uint256 i = 0; i < pools.length; i++) { Pool memory pool = pools[i]; IMasterChef.PoolInfo memory poolInfo = IMasterChef(masterChef).poolInfo(pool.pid); @@ -76,7 +78,6 @@ contract PoolRewardDistributor is BoringOwnable { } function _removePool(uint256 _pid) internal { - require(_hasPool(_pid), 'pool not found'); uint256 index = _poolIndex(_pid); Pool memory pool = pools[index]; @@ -92,21 +93,12 @@ contract PoolRewardDistributor is BoringOwnable { } function _poolIndex(uint256 _pid) internal view returns (uint256) { - require(_hasPool(_pid), 'pool not found'); - for (uint256 i = 0; i < pools.length; i++) { if (pools[i].pid == _pid) { return i; } } - } - function _hasPool(uint256 _pid) internal view returns (bool) { - for (uint256 i = 0; i < pools.length; i++) { - if (pools[i].pid == _pid) { - return true; - } - } - return false; + revert('pool not found'); } }