diff --git a/README.md b/README.md index e14aed9..93fd1de 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,12 @@ -# DODO V2: Help 1 Trillion People Issue Token +# Scope of Audit -## Audit Report +- contracts/DODOToken/DODOMineV3/ -[Audited by Peckshield](https://github.com/DODOEX/contractV2/blob/main/audit/PeckShield-Audit-DODOV2-v1.0.pdf) +- contracts/Factory/Registries/DODOMineV3Registry.sol -## Bug Bounty 💰 +- contracts/SmartRoute/proxies/DODOMineV3Proxy.sol -### Rewards +- contracts/external/CustomERC20.sol -Severity of bugs will be assessed under the [CVSS Risk Rating](https://www.first.org/cvss/calculator/3.0) scale, as follows: - - - Critical (9.0-10.0): Up to $100,000 - - High (7.0-8.9): Up to $10,000 - - Medium (4.0-6.9): Up to $5,000 - - Low (0.1-3.9): Up to $1,000 - -In addition to assessing severity, rewards will be considered based on the impact of the discovered vulnerability as well as the level of difficulty in discovering such vulnerability. - -### Disclosure - -Any vulnerability or bug discovered must be reported only to the following email: contact@dodoex.io; must not be disclosed publicly; must not be disclosed to any other person, entity or email address prior to disclosure to the contact@dodoex.io email; and must not be disclosed in any way other than to the contact@dodoex.io email. In addition, disclosure to contact@dodoex.io must be made promptly following discovery of the vulnerability. Please include as much information about the vulnerability as possible, including: - - - The conditions on which reproducing the bug is contingent. - - The steps needed to reproduce the bug or, preferably, a proof of concept. - - The potential implications of the vulnerability being abused. - -A detailed report of a vulnerability increases the likelihood of a reward and may increase the reward amount. - -Anyone who reports a unique, previously-unreported vulnerability that results in a change to the code or a configuration change and who keeps such vulnerability confidential until it has been resolved by our engineers will be recognized publicly for their contribution, if agreed. - -## Contact Us - -Send E-mail to contact@dodoex.io +- contracts/Factory/ERC20V2Factory.sol diff --git a/contracts/DODOToken/DODOMineV3/BaseMine.sol b/contracts/DODOToken/DODOMineV3/BaseMine.sol index 4f62301..bab854a 100644 --- a/contracts/DODOToken/DODOMineV3/BaseMine.sol +++ b/contracts/DODOToken/DODOMineV3/BaseMine.sol @@ -172,6 +172,10 @@ contract BaseMine is InitializableOwnable { rt.rewardPerBlock = rewardPerBlock; rt.rewardVault = address(new RewardVault(rewardToken)); + uint256 rewardAmount = rewardPerBlock.mul(endBlock.sub(startBlock)); + IERC20(rewardToken).transfer(rt.rewardVault, rewardAmount); + RewardVault(rt.rewardVault).syncValue(); + emit NewRewardToken(len, rewardToken); } @@ -183,6 +187,12 @@ contract BaseMine is InitializableOwnable { _updateReward(address(0), i); RewardTokenInfo storage rt = rewardTokenInfos[i]; + + uint256 totalDepositReward = RewardVault(rt.rewardVault)._TOTAL_REWARD_(); + uint256 gap = newEndBlock.sub(rt.lastFlagBlock); + uint256 totalReward = rt.workThroughReward.add(gap.mul(rt.rewardPerBlock)); + require(totalDepositReward >= totalReward, "DODOMineV3: REWARD_NOT_ENOUGH"); + require(block.number < newEndBlock, "DODOMineV3: END_BLOCK_INVALID"); require(block.number > rt.startBlock, "DODOMineV3: NOT_START"); require(block.number < rt.endBlock, "DODOMineV3: ALREADY_CLOSE"); @@ -205,6 +215,11 @@ contract BaseMine is InitializableOwnable { rt.rewardPerBlock = newRewardPerBlock; rt.lastFlagBlock = block.number; + uint256 totalDepositReward = RewardVault(rt.rewardVault)._TOTAL_REWARD_(); + uint256 gap = rt.endBlock.sub(block.number); + uint256 totalReward = rt.workThroughReward.add(gap.mul(newRewardPerBlock)); + require(totalDepositReward >= totalReward, "DODOMineV3: REWARD_NOT_ENOUGH"); + emit UpdateReward(i, newRewardPerBlock); } diff --git a/contracts/DODOToken/DODOMineV3/RewardVault.sol b/contracts/DODOToken/DODOMineV3/RewardVault.sol index 25ca614..e93ed3e 100644 --- a/contracts/DODOToken/DODOMineV3/RewardVault.sol +++ b/contracts/DODOToken/DODOMineV3/RewardVault.sol @@ -16,7 +16,7 @@ import {IERC20} from "../../intf/IERC20.sol"; interface IRewardVault { function reward(address to, uint256 amount) external; function withdrawLeftOver(address to, uint256 amount) external; - function depositReward() external; + function syncValue() external; function _TOTAL_REWARD_() external view returns(uint256); } @@ -47,7 +47,7 @@ contract RewardVault is Ownable { IERC20(_REWARD_TOKEN_).safeTransfer(to, amount); } - function depositReward() external { + function syncValue() external { uint256 rewardBalance = IERC20(_REWARD_TOKEN_).balanceOf(address(this)); uint256 rewardInput = rewardBalance.sub(_REWARD_RESERVE_); diff --git a/contracts/Factory/DODOMineV3Factory.sol b/contracts/Factory/DODOMineV3Factory.sol deleted file mode 100644 index 70f56a4..0000000 --- a/contracts/Factory/DODOMineV3Factory.sol +++ /dev/null @@ -1,162 +0,0 @@ -/* - - Copyright 2021 DODO ZOO. - SPDX-License-Identifier: Apache-2.0 - -*/ - -pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; -import {IDODOApproveProxy} from "../SmartRoute/DODOApproveProxy.sol"; -import {IRewardVault} from "../DODOToken/DODOMineV3/RewardVault.sol"; -import {ICloneFactory} from "../lib/CloneFactory.sol"; -import {SafeMath} from "../lib/SafeMath.sol"; - -interface IMineV3 { - function init(address owner, address token) external; - - function addRewardToken( - address rewardToken, - uint256 rewardPerBlock, - uint256 startBlock, - uint256 endBlock - ) external; - - function getVaultByRewardToken(address rewardToken) external view returns(address); - - function directTransferOwnership(address newOwner) external; -} - -/** - * @title DODOMineV3 Factory - * @author DODO Breeder - * - * @notice Create And Register DODOMineV3 Contracts - */ -contract DODOMineV3Factory is InitializableOwnable { - using SafeMath for uint256; - // ============ Templates ============ - - address public immutable _CLONE_FACTORY_; - address public _MINEV3_TEMPLATE_; - address public _DODO_APPROVE_PROXY_; - mapping (address => bool) public singleTokenList; - - // minePool -> stakeToken - mapping(address => address) public _MINE_REGISTRY_; - // lpToken -> minePool - mapping(address => address) public _LP_REGISTRY_; - // singleToken -> minePool - mapping(address => address[]) public _SINGLE_REGISTRY_; - - // ============ Events ============ - - event NewMineV3(address mine, address stakeToken, bool isLpToken); - event RemoveMineV3(address mine, address stakeToken); - - constructor( - address cloneFactory, - address mineTemplate, - address dodoApproveProxy - ) public { - _CLONE_FACTORY_ = cloneFactory; - _MINEV3_TEMPLATE_ = mineTemplate; - _DODO_APPROVE_PROXY_ = dodoApproveProxy; - } - - // ============ Functions ============ - - function createDODOMineV3( - address stakeToken, - bool isLpToken, - address[] memory rewardTokens, - uint256[] memory rewardPerBlock, - uint256[] memory startBlock, - uint256[] memory endBlock - ) external returns (address newMineV3) { - require(rewardTokens.length > 0, "REWARD_EMPTY"); - require(rewardTokens.length == rewardPerBlock.length, "REWARD_PARAM_NOT_MATCH"); - require(startBlock.length == rewardPerBlock.length, "REWARD_PARAM_NOT_MATCH"); - require(endBlock.length == rewardPerBlock.length, "REWARD_PARAM_NOT_MATCH"); - - newMineV3 = ICloneFactory(_CLONE_FACTORY_).clone(_MINEV3_TEMPLATE_); - - IMineV3(newMineV3).init(address(this), stakeToken); - - for(uint i = 0; i bool) public isAdminListed; + mapping (address => bool) public singleTokenList; + + // ============ Registry ============ + // minePool -> stakeToken + mapping(address => address) public _MINE_REGISTRY_; + // lpToken -> minePool + mapping(address => address) public _LP_REGISTRY_; + // singleToken -> minePool + mapping(address => address[]) public _SINGLE_REGISTRY_; + + + // ============ Events ============ + event NewMineV3(address mine, address stakeToken, bool isLpToken); + event RemoveMineV3(address mine, address stakeToken); + + + function addMineV3( + address mine, + bool isLpToken, + address stakeToken + ) override external { + require(isAdminListed[msg.sender], "ACCESS_DENIED"); + _MINE_REGISTRY_[mine] = stakeToken; + if(isLpToken) { + _LP_REGISTRY_[stakeToken] = mine; + }else { + require(_SINGLE_REGISTRY_[stakeToken].length == 0 || singleTokenList[stakeToken], "ALREADY_EXSIT_POOL"); + _SINGLE_REGISTRY_[stakeToken].push(mine); + } + + emit NewMineV3(mine, stakeToken, isLpToken); + } + + // ============ Admin Operation Functions ============ + + function removeMineV3( + address mine, + bool isLpToken, + address stakeToken + ) external onlyOwner { + _MINE_REGISTRY_[mine] = address(0); + if(isLpToken) { + _LP_REGISTRY_[stakeToken] = address(0); + }else { + uint256 len = _SINGLE_REGISTRY_[stakeToken].length; + for (uint256 i = 0; i < len; i++) { + if (mine == _SINGLE_REGISTRY_[stakeToken][i]) { + if(i != len - 1) { + _SINGLE_REGISTRY_[stakeToken][i] = _SINGLE_REGISTRY_[stakeToken][len - 1]; + } + _SINGLE_REGISTRY_[stakeToken].pop(); + break; + } + } + } + + emit RemoveMineV3(mine, stakeToken); + } + + function addAdminList (address contractAddr) external onlyOwner { + isAdminListed[contractAddr] = true; + } + + function removeAdminList (address contractAddr) external onlyOwner { + isAdminListed[contractAddr] = false; + } + + function addSingleTokenList(address token) external onlyOwner { + singleTokenList[token] = true; + } + + function removeSingleTokenList(address token) external onlyOwner { + singleTokenList[token] = false; + } +} \ No newline at end of file diff --git a/contracts/SmartRoute/proxies/DODOMineV3Proxy.sol b/contracts/SmartRoute/proxies/DODOMineV3Proxy.sol new file mode 100644 index 0000000..5266cd1 --- /dev/null +++ b/contracts/SmartRoute/proxies/DODOMineV3Proxy.sol @@ -0,0 +1,131 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; +import {IDODOApproveProxy} from "../DODOApproveProxy.sol"; +import {IRewardVault} from "../../DODOToken/DODOMineV3/RewardVault.sol"; +import {IDODOMineV3Registry} from "../../Factory/Registries/DODOMineV3Registry.sol"; +import {ICloneFactory} from "../../lib/CloneFactory.sol"; +import {SafeMath} from "../../lib/SafeMath.sol"; + +interface IMineV3 { + function init(address owner, address token) external; + + function addRewardToken( + address rewardToken, + uint256 rewardPerBlock, + uint256 startBlock, + uint256 endBlock + ) external; + + function directTransferOwnership(address newOwner) external; + + function getVaultByRewardToken(address rewardToken) external view returns(address); +} + +/** + * @title DODOMineV3 Proxy + * @author DODO Breeder + * + * @notice Create And Register DODOMineV3 Contracts + */ +contract DODOMineV3Proxy is InitializableOwnable { + using SafeMath for uint256; + // ============ Templates ============ + + address public immutable _CLONE_FACTORY_; + address public immutable _DODO_APPROVE_PROXY_; + address public immutable _DODO_MINEV3_REGISTRY_; + address public _MINEV3_TEMPLATE_; + + + // ============ Events ============ + event DepositRewardToVault(address mine, address rewardToken, uint256 amount); + event DepositRewardToMine(address mine, address rewardToken, uint256 amount); + event CreateMineV3(address account, address mineV3); + + constructor( + address cloneFactory, + address mineTemplate, + address dodoApproveProxy, + address dodoMineV3Registry + ) public { + _CLONE_FACTORY_ = cloneFactory; + _MINEV3_TEMPLATE_ = mineTemplate; + _DODO_APPROVE_PROXY_ = dodoApproveProxy; + _DODO_MINEV3_REGISTRY_ = dodoMineV3Registry; + } + + // ============ Functions ============ + + function createDODOMineV3( + address stakeToken, + bool isLpToken, + address[] memory rewardTokens, + uint256[] memory rewardPerBlock, + uint256[] memory startBlock, + uint256[] memory endBlock + ) external returns (address newMineV3) { + require(rewardTokens.length > 0, "REWARD_EMPTY"); + require(rewardTokens.length == rewardPerBlock.length, "REWARD_PARAM_NOT_MATCH"); + require(startBlock.length == rewardPerBlock.length, "REWARD_PARAM_NOT_MATCH"); + require(endBlock.length == rewardPerBlock.length, "REWARD_PARAM_NOT_MATCH"); + + newMineV3 = ICloneFactory(_CLONE_FACTORY_).clone(_MINEV3_TEMPLATE_); + + IMineV3(newMineV3).init(address(this), stakeToken); + + for(uint i = 0; i