From 58962505eb99c2e3ec93c4117af4dfbe36c6eaef Mon Sep 17 00:00:00 2001 From: mingda Date: Sat, 23 Jan 2021 18:34:22 +0800 Subject: [PATCH 01/42] update reademe --- README.md | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 60d9b85..844ffcb 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,30 @@ -# DODOV2 +# DODO V2: Help 1 Trillion People Issue Token -## DVM +## Bug Bounty 💰 -DVM 是 DODO Vending Machine的缩写,”自动售货机“ +### Rewards -这一模式下 +Severity of bugs will be assessed under the [CVSS Risk Rating](https://www.first.org/cvss/calculator/3.0) scale, as follows: - 1. 参数i和k不变 - 2. 任何人都能充值 - 3. 第一次充值不需要按照比例,后续需要按照比例充值 + - 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 -## DPP +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. -DPP 是 DODO Private Pool 的缩写,”私有池“ +### 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: - 1. 参数i和k可以任意改变 - 2. Target和reserve可以任意设置 - 3. 只有creator可以充提,且充提不受限制 + - 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. -https://dodoex.github.io/docs/docs/coreConcept \ No newline at end of file +## Contact Us + +Send E-mail to contact@dodoex.io \ No newline at end of file From 199ea5f1d539fec88e8b45d226a924126ccc5d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Fri, 29 Jan 2021 16:27:05 +0800 Subject: [PATCH 02/42] upload VDODO --- contracts/VDODO/VDODOChef.sol | 1057 +++++++++++++++++++++++++++++++++ 1 file changed, 1057 insertions(+) create mode 100644 contracts/VDODO/VDODOChef.sol diff --git a/contracts/VDODO/VDODOChef.sol b/contracts/VDODO/VDODOChef.sol new file mode 100644 index 0000000..fda7398 --- /dev/null +++ b/contracts/VDODO/VDODOChef.sol @@ -0,0 +1,1057 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +/** + * @title SafeMath + * @author DODO Breeder + * + * @notice Math operations with safety checks that revert on error + */ +library SafeMath { + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, "MUL_ERROR"); + + return c; + } + + function div(uint256 a, uint256 b) internal pure returns (uint256) { + require(b > 0, "DIVIDING_ERROR"); + return a / b; + } + + function divCeil(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 quotient = div(a, b); + uint256 remainder = a - quotient * b; + if (remainder > 0) { + return quotient + 1; + } else { + return quotient; + } + } + + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + require(b <= a, "SUB_ERROR"); + return a - b; + } + + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, "ADD_ERROR"); + return c; + } + + function sqrt(uint256 x) internal pure returns (uint256 y) { + uint256 z = x / 2 + 1; + y = x; + while (z < y) { + y = z; + z = (x / z + z) / 2; + } + } +} + +pragma solidity 0.6.9; +/** + * @title DecimalMath + * @author DODO Breeder + * + * @notice Functions for fixed point number with 18 decimals + */ +library DecimalMath { + using SafeMath for uint256; + + uint256 internal constant ONE = 10**18; + uint256 internal constant ONE2 = 10**36; + + function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) { + return target.mul(d) / (10**18); + } + + function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) { + return target.mul(d).divCeil(10**18); + } + + function divFloor(uint256 target, uint256 d) internal pure returns (uint256) { + return target.mul(10**18).div(d); + } + + function divCeil(uint256 target, uint256 d) internal pure returns (uint256) { + return target.mul(10**18).divCeil(d); + } + + function reciprocalFloor(uint256 target) internal pure returns (uint256) { + return uint256(10**36).div(target); + } + + function reciprocalCeil(uint256 target) internal pure returns (uint256) { + return uint256(10**36).divCeil(target); + } +} +// File: @openzeppelin/contracts/access/Ownable.sol + + + +pragma solidity 0.6.9; +/** + * @title Ownable + * @author DODO Breeder + * + * @notice Ownership related functions + */ +contract Ownable { + address public _OWNER_; + address public _NEW_OWNER_; + + // ============ Events ============ + + event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner); + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + // ============ Modifiers ============ + + modifier onlyOwner() { + require(msg.sender == _OWNER_, "NOT_OWNER"); + _; + } + + // ============ Functions ============ + + constructor() internal { + _OWNER_ = msg.sender; + emit OwnershipTransferred(address(0), _OWNER_); + } + + function transferOwnership(address newOwner) external onlyOwner { + emit OwnershipTransferPrepared(_OWNER_, newOwner); + _NEW_OWNER_ = newOwner; + } + + function claimOwnership() external { + require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM"); + emit OwnershipTransferred(_OWNER_, _NEW_OWNER_); + _OWNER_ = _NEW_OWNER_; + _NEW_OWNER_ = address(0); + } +} + + +pragma solidity 0.6.9; + +/** + * @title ReentrancyGuard + * @author DODO Breeder + * + * @notice Protect functions from Reentrancy Attack + */ +contract ReentrancyGuard { + // https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=zero-state#scoping-and-declarations + // zero-state of _ENTERED_ is false + bool private _ENTERED_; + + modifier preventReentrant() { + require(!_ENTERED_, "REENTRANT"); + _ENTERED_ = true; + _; + _ENTERED_ = false; + } +} + +// File: @openzeppelin/contracts/token/ERC20/IERC20.sol + + + +pragma solidity 0.6.9; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +pragma solidity 0.6.9; + +/* + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with GSN meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address payable) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes memory) { + this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 + return msg.data; + } +} + +pragma solidity 0.6.9; +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using SafeMath for uint256; + + function safeTransfer( + IERC20 token, + address to, + uint256 value + ) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom( + IERC20 token, + address from, + address to, + uint256 value + ) internal { + _callOptionalReturn( + token, + abi.encodeWithSelector(token.transferFrom.selector, from, to, value) + ); + } + + function safeApprove( + IERC20 token, + address spender, + uint256 value + ) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + // solhint-disable-next-line max-line-length + require( + (value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. + + // A Solidity high level call has three parts: + // 1. The target address is checked to verify it contains contract code + // 2. The call itself is made, and success asserted + // 3. The return value is decoded, which in turn checks the size of the returned data. + // solhint-disable-next-line max-line-length + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = address(token).call(data); + require(success, "SafeERC20: low-level call failed"); + + if (returndata.length > 0) { + // Return data is optional + // solhint-disable-next-line max-line-length + require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); + } + } +} + +// File: @openzeppelin/contracts/utils/Address.sol +pragma solidity 0.6.9; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // According to EIP-1052, 0x0 is the value returned for not-yet created accounts + // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned + // for accounts without code, i.e. `keccak256('')` + bytes32 codehash; + bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; + // solhint-disable-next-line no-inline-assembly + assembly { codehash := extcodehash(account) } + return (codehash != accountHash && codehash != 0x0); + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{ value: amount }(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain`call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + return _functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + return _functionCallWithValue(target, data, value, errorMessage); + } + + function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { + require(isContract(target), "Address: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} + +pragma solidity 0.6.9; + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20PresetMinterPauser}. + * + * TIP: For a detailed writeup see our guide + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * We have followed general OpenZeppelin guidelines: functions revert instead + * of returning `false` on failure. This behavior is nonetheless conventional + * and does not conflict with the expectations of ERC20 applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + */ +contract ERC20 is Context, IERC20 { + using SafeMath for uint256; + using Address for address; + + bool cantransfer; + uint256 private _totalSupply; + string private _name; + string private _symbol; + uint8 private _decimals; + + mapping (address => uint256) private _balances; + mapping (address => mapping (address => uint256)) private _allowances; + mapping(address => bool) public operater; + // ============ Modifiers ============ + modifier onlyOperater() { + require(cantransfer || operater[msg.sender] , "not allowed transfer"); + _; + } + + /** + * @dev Sets the values for {name} and {symbol}, initializes {decimals} with + * a default value of 18. + * + * To select a different value for {decimals}, use {_setupDecimals}. + * + * All three of these values are immutable: they can only be set once during + * construction. + */ + constructor (string memory name, string memory symbol) public { + _name = name; + _symbol = symbol; + _decimals = 18; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5,05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is + * called. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view returns (uint8) { + return _decimals; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view override returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual override returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `recipient` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(_msgSender(), recipient, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + _approve(_msgSender(), spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}; + * + * Requirements: + * - `sender` and `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + * - the caller must have allowance for ``sender``'s tokens of at least + * `amount`. + */ + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount)); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue)); + return true; + } + + /** + * @dev Moves tokens `amount` from `sender` to `recipient`. + * + * This is internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `sender` cannot be the zero address. + * - `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + */ + function _transfer(address sender, address recipient, uint256 amount) internal onlyOperater virtual { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount); + _balances[recipient] = _balances[recipient].add(amount); + emit Transfer(sender, recipient, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements + * + * - `to` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + _balances[account] = _balances[account].sub(amount); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. + * + * This is internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve(address owner, address spender, uint256 amount) internal onlyOperater virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + /** + * @dev Sets {decimals} to a value other than the default one of 18. + * + * WARNING: This function should only be called from the constructor. Most + * applications that interact with token contracts will not expect + * {decimals} to ever change, and may work incorrectly if it does. + */ + function _setupDecimals(uint8 decimals_) internal { + _decimals = decimals_; + } + + function _setCantransfer(bool _cantransfer) internal { + cantransfer = _cantransfer; + } + + function _addOperation(address _operater) internal { + operater[_operater] = true; + } + function _removeOperation(address _operater) internal { + operater[_operater] = false; + } + + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be to transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } +} +pragma solidity 0.6.9; +interface IGovernance { + function governanceCall(address account, uint256 amount,bytes calldata data) external returns (bool); + +} +pragma solidity 0.6.9; +contract SpToken is ERC20("StakingPowerToken", "SPT"), Ownable ,ReentrancyGuard{ + using SafeMath for uint256; + using SafeERC20 for IERC20; + + address govAddr; + IERC20 dodo; + + uint256 public alpha = 1; + uint256 public totalSp; + uint256 public totalOverdraft; + + uint256 public _DODOPERBLOCK_ = 1e18;//TODO + uint256 constant public _MAG_SP_AMOUNT_ = 10; + uint256 constant public _MAG_TOTALSP_AMOUNT_ = 110; + uint256 constant public _BASE_AMOUNT_ = 100; + + struct UserInfo { + uint256 dodoAmount; + address upline; + uint256 spAmount; + uint256 overdraft; + uint256 lastRewardBlock; + uint256 totalRedeem; + bool hasParticipateGov; //是否正在参与治理,是的话就不可以提币 + } + + mapping (address => UserInfo) public userInfo; + +// ============ Events ============ + event ParticipatoryGov(address indexed user, uint256 amount); + event Deposit(address indexed user,address indexed upline, uint256 amount); + event Redeem(address indexed user, uint256 amount); + event SetCantransfer(bool allowed); + event RemoveOperation(address indexed _operater); + event AddOperation(address indexed _operater); + + receive() external payable { + revert(); + } + // ============ Functions ============ + constructor( + address _govAddr, + address _dodo + + ) public { + govAddr = _govAddr; + dodo = IERC20(_dodo); + } + + + function mint(address _to, uint256 _amount) public onlyOwner { + _mint(_to, _amount); + } + function burn(address _to, uint256 amount) public onlyOwner{ + _burn(_to,amount); + } + function setCantransfer(bool _allowed) public onlyOwner { + _setCantransfer(_allowed); + emit SetCantransfer(_allowed); + } + function addOperationAddress(address _operationAddress) public onlyOwner { + _addOperation(_operationAddress); + emit AddOperation(_operationAddress); + } + function removeOperation(address _operationAddress) public onlyOwner { + _removeOperation(_operationAddress); + emit RemoveOperation(_operationAddress); + } + + function participatoryGov( + uint256 _amount, + bytes calldata _data + ) external preventReentrant { + UserInfo storage user = userInfo[msg.sender]; + require(user.spAmount>_amount,"no enough sp"); + if (_data.length > 0) + IGovernance(govAddr).governanceCall(msg.sender, _amount, _data); + user.spAmount = user.spAmount.sub(_amount); + user.hasParticipateGov = true; + emit ParticipatoryGov(msg.sender, _amount); + } + + //TODO _uplineAddress??? deposit again???? + //TODO round up /down + function deposit(uint256 _amount,address _uplineAddress) public preventReentrant{ + require(_amount>0,"must deposit greater than 0"); + + dodo.transferFrom(msg.sender, address(this), _amount); + + UserInfo storage user = userInfo[msg.sender]; + if(user.dodoAmount==0){ + user.lastRewardBlock = block.number; + } + user.dodoAmount = user.dodoAmount.add(_amount); + // accuDODO = _DODOPERBLOCK_*(block-lastRewardBlock) + uint256 accuDODO = _DODOPERBLOCK_ * (block.number.sub(user.lastRewardBlock)); + //TODO FIRST DEPOSIT??? + if(totalSp > 0){ + // alpha = alpha + accuDODO/totalSp (round down) + alpha = alpha.add(accuDODO.div(totalSp)); + } + + // 自己的sp + x/alpha + uint256 newSpAmount = _amount.div(alpha); + + _mint(msg.sender,newSpAmount); + // spToken.mint(msg.sender,newSpAmount); + + user.spAmount = user.spAmount.add(newSpAmount); + if(user.upline==address(0x0)){ + user.upline = _uplineAddress; + } + UserInfo storage uplineUser = userInfo[user.upline]; + // 上级sp +( x/alpha)* 0.1 (round up) + uplineUser.spAmount = uplineUser.spAmount.add( + _amount.mul(alpha) + .mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); + + // 上级DODO欠款 + x*0.1 (round up) + uint256 overdraft = _amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_); + uplineUser.overdraft = uplineUser.overdraft.add(overdraft); + + totalOverdraft = totalOverdraft.add(overdraft); + // total sp + x/alpha*1.1 + totalSp = totalSp.add(_amount.div(alpha).mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); + emit Deposit(msg.sender,_uplineAddress, _amount); + } + //TODO round up /down + + function redeem(uint256 _amount) public preventReentrant{ + UserInfo storage user = userInfo[msg.sender]; + require(user.spAmount>_amount,"no enough sp token"); + require(!user.hasParticipateGov,"hasParticipateGov"); + + + // accuDODO = _DODOPERBLOCK_*(block-lastRewardBlock) + uint256 accuDODO = _DODOPERBLOCK_ * (block.number.sub(user.lastRewardBlock)); + // alpha = alpha + accuDODO/totalSp (round down) + alpha = alpha.add(accuDODO.div(totalSp)); + // 自己的sp - x + _burn(msg.sender,_amount); + // spToken.burn(msg.sender,_amount); + + user.spAmount = user.spAmount.sub(_amount); + + UserInfo storage uplineUser = userInfo[user.upline]; + // 上级sp - (x)*0.1(round down) + uplineUser.spAmount = uplineUser.spAmount.sub( + _amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); + + // 上级DODO欠款 - x*alpha*0.1 (round down) + uint256 overdraft = _amount.mul(alpha).mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_); + uplineUser.overdraft = uplineUser.overdraft.sub(overdraft); + + totalOverdraft = totalOverdraft.sub(overdraft); + + // total sp - x*1.1 + totalSp = totalSp.sub(_amount.mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); + + user.lastRewardBlock = block.number; + + uint256 feeRatio = checkReward(_amount); + + // x * 80% transfer to user + uint256 receiveAmount = _amount.mul(_BASE_AMOUNT_.sub(feeRatio).div(_BASE_AMOUNT_)); + + dodo.safeTransferFrom(address(this), msg.sender, receiveAmount); + user.dodoAmount = user.dodoAmount.sub(receiveAmount); + user.totalRedeem = user.totalRedeem.add(receiveAmount); + + // alpha = alpha + x * 20% /totalSp + uint256 feeAmount = _amount.mul(feeRatio.div(_BASE_AMOUNT_)); + alpha = alpha.add(feeAmount.div(totalSp)); + + //TODO 3. 这部分税会继续拆成两部分,第一部分销毁,第二部分分给所有vDODO持有人 + + + emit Redeem(msg.sender, _amount); + } + + //TODO + function checkReward(uint256 _amount) internal returns(uint256) { + + // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 (5≤ y ≤ 15) + + // y = 5 (x ≤ 1) + + // y = 15 (x ≥ 10) + } + + // balanceOf = sp-DODO欠款/alpha + function balanceOf(address _address) public view override returns (uint256 balance) { + UserInfo memory user = userInfo[_address]; + balance = user.spAmount.sub(user.overdraft.div(alpha)); + } + + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(_msgSender(), recipient, amount); + return true; + } + + function _transfer(address sender, address recipient, uint256 _amount) internal onlyOperater virtual override { + require(sender != address(0), " transfer from the zero address"); + require(recipient != address(0), " transfer to the zero address"); + // require(balanceOf(from)≥amount) + require(balanceOf(sender) >= _amount,"no enough to sp transfer"); + UserInfo storage user = userInfo[sender]; + // sp[msg.sender] -= amount + user.spAmount = user.spAmount.sub(_amount); + + // sp[上级[from]] -= amount*0.1 (round down) + UserInfo storage uplineUser = userInfo[user.upline]; + uplineUser.spAmount = uplineUser.spAmount.sub(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); + + UserInfo storage recipientUser = userInfo[recipient]; + // sp[to] += amount + recipientUser.spAmount = recipientUser.spAmount.add(_amount); + + UserInfo storage recipientUplineUser = userInfo[recipientUser.upline]; + recipientUplineUser.spAmount =recipientUplineUser.spAmount.add(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); + emit Transfer(sender, recipient, _amount); + } + + // 可提取DODO数额 = sp*alpha - DODO欠款 + function canWithDraw(address _address) public view returns (uint256 withDrawAmount) { + UserInfo memory user = userInfo[_address]; + withDrawAmount = user.spAmount.mul(alpha).sub(user.overdraft); + } + + function checkUserInfo(address _userAddress) public view returns(uint256,address,uint256,uint256,uint256,uint256,bool) { + UserInfo memory user = userInfo[_userAddress]; + return (user.dodoAmount, user.upline, user.spAmount, user.overdraft,user.lastRewardBlock,user.totalRedeem,user.hasParticipateGov); + + } + + + +} +// deposit 是否需要输入上级地址 +// round up & round down +//vDODO的分红 +// + From 7c8b443d2b4a521a6c5b61a70adebe940b705a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Sun, 31 Jan 2021 16:22:15 +0800 Subject: [PATCH 03/42] Update VDODOChef.sol --- contracts/VDODO/VDODOChef.sol | 1230 ++++++++------------------------- 1 file changed, 284 insertions(+), 946 deletions(-) diff --git a/contracts/VDODO/VDODOChef.sol b/contracts/VDODO/VDODOChef.sol index fda7398..fbc80e0 100644 --- a/contracts/VDODO/VDODOChef.sol +++ b/contracts/VDODO/VDODOChef.sol @@ -4,1054 +4,392 @@ SPDX-License-Identifier: Apache-2.0 */ +import {IERC20} from "../intf/IERC20.sol"; +import {Address} from "../lib/Address.sol"; +import {SafeMath} from "../lib/SafeMath.sol"; +import {DecimalMath} from "../lib/DecimalMath.sol"; +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; +import {SafeERC20} from "../lib/SafeERC20.sol"; +import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -/** - * @title SafeMath - * @author DODO Breeder - * - * @notice Math operations with safety checks that revert on error - */ -library SafeMath { - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - if (a == 0) { - return 0; - } - - uint256 c = a * b; - require(c / a == b, "MUL_ERROR"); - - return c; - } - - function div(uint256 a, uint256 b) internal pure returns (uint256) { - require(b > 0, "DIVIDING_ERROR"); - return a / b; - } - - function divCeil(uint256 a, uint256 b) internal pure returns (uint256) { - uint256 quotient = div(a, b); - uint256 remainder = a - quotient * b; - if (remainder > 0) { - return quotient + 1; - } else { - return quotient; - } - } - - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - require(b <= a, "SUB_ERROR"); - return a - b; - } - - function add(uint256 a, uint256 b) internal pure returns (uint256) { - uint256 c = a + b; - require(c >= a, "ADD_ERROR"); - return c; - } - - function sqrt(uint256 x) internal pure returns (uint256 y) { - uint256 z = x / 2 + 1; - y = x; - while (z < y) { - y = z; - z = (x / z + z) / 2; - } - } +interface IGovernance { + function governanceCall(address account, uint256 amount,bytes calldata data) external returns (bool); } pragma solidity 0.6.9; -/** - * @title DecimalMath - * @author DODO Breeder - * - * @notice Functions for fixed point number with 18 decimals - */ -library DecimalMath { +interface IHelper { + function getDodoAmount() external returns (uint256); +} +pragma solidity 0.6.9; +contract SpToken is IERC20,InitializableOwnable ,ReentrancyGuard{ using SafeMath for uint256; + using SafeERC20 for IERC20; - uint256 internal constant ONE = 10**18; - uint256 internal constant ONE2 = 10**36; - - function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(d) / (10**18); - } - - function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(d).divCeil(10**18); - } - - function divFloor(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(10**18).div(d); - } - - function divCeil(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(10**18).divCeil(d); - } - - function reciprocalFloor(uint256 target) internal pure returns (uint256) { - return uint256(10**36).div(target); - } - - function reciprocalCeil(uint256 target) internal pure returns (uint256) { - return uint256(10**36).divCeil(target); - } -} -// File: @openzeppelin/contracts/access/Ownable.sol - - - -pragma solidity 0.6.9; -/** - * @title Ownable - * @author DODO Breeder - * - * @notice Ownership related functions - */ -contract Ownable { - address public _OWNER_; - address public _NEW_OWNER_; - - // ============ Events ============ - - event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner); - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - // ============ Modifiers ============ - - modifier onlyOwner() { - require(msg.sender == _OWNER_, "NOT_OWNER"); - _; - } - - // ============ Functions ============ - - constructor() internal { - _OWNER_ = msg.sender; - emit OwnershipTransferred(address(0), _OWNER_); - } - - function transferOwnership(address newOwner) external onlyOwner { - emit OwnershipTransferPrepared(_OWNER_, newOwner); - _NEW_OWNER_ = newOwner; - } - - function claimOwnership() external { - require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM"); - emit OwnershipTransferred(_OWNER_, _NEW_OWNER_); - _OWNER_ = _NEW_OWNER_; - _NEW_OWNER_ = address(0); - } -} - - -pragma solidity 0.6.9; - -/** - * @title ReentrancyGuard - * @author DODO Breeder - * - * @notice Protect functions from Reentrancy Attack - */ -contract ReentrancyGuard { - // https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=zero-state#scoping-and-declarations - // zero-state of _ENTERED_ is false - bool private _ENTERED_; - - modifier preventReentrant() { - require(!_ENTERED_, "REENTRANT"); - _ENTERED_ = true; - _; - _ENTERED_ = false; - } -} - -// File: @openzeppelin/contracts/token/ERC20/IERC20.sol - - - -pragma solidity 0.6.9; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address recipient, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `sender` to `recipient` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); - - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); -} - -pragma solidity 0.6.9; - -/* - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with GSN meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address payable) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes memory) { - this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 - return msg.data; - } -} - -pragma solidity 0.6.9; -/** - * @title SafeERC20 - * @dev Wrappers around ERC20 operations that throw on failure (when the token - * contract returns false). Tokens that return no value (and instead revert or - * throw on failure) are also supported, non-reverting calls are assumed to be - * successful. - * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, - * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. - */ -library SafeERC20 { - using SafeMath for uint256; - - function safeTransfer( - IERC20 token, - address to, - uint256 value - ) internal { - _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); - } - - function safeTransferFrom( - IERC20 token, - address from, - address to, - uint256 value - ) internal { - _callOptionalReturn( - token, - abi.encodeWithSelector(token.transferFrom.selector, from, to, value) - ); - } - - function safeApprove( - IERC20 token, - address spender, - uint256 value - ) internal { - // safeApprove should only be called when setting an initial allowance, - // or when resetting it to zero. To increase and decrease it, use - // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' - // solhint-disable-next-line max-line-length - require( - (value == 0) || (token.allowance(address(this), spender) == 0), - "SafeERC20: approve from non-zero to non-zero allowance" - ); - _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); - } - - /** - * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement - * on the return value: the return value is optional (but if data is returned, it must not be false). - * @param token The token targeted by the call. - * @param data The call data (encoded using abi.encode or one of its variants). - */ - function _callOptionalReturn(IERC20 token, bytes memory data) private { - // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since - // we're implementing it ourselves. - - // A Solidity high level call has three parts: - // 1. The target address is checked to verify it contains contract code - // 2. The call itself is made, and success asserted - // 3. The return value is decoded, which in turn checks the size of the returned data. - // solhint-disable-next-line max-line-length - - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory returndata) = address(token).call(data); - require(success, "SafeERC20: low-level call failed"); - - if (returndata.length > 0) { - // Return data is optional - // solhint-disable-next-line max-line-length - require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); - } - } -} - -// File: @openzeppelin/contracts/utils/Address.sol -pragma solidity 0.6.9; - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - */ - function isContract(address account) internal view returns (bool) { - // According to EIP-1052, 0x0 is the value returned for not-yet created accounts - // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned - // for accounts without code, i.e. `keccak256('')` - bytes32 codehash; - bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; - // solhint-disable-next-line no-inline-assembly - assembly { codehash := extcodehash(account) } - return (codehash != accountHash && codehash != 0x0); - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - // solhint-disable-next-line avoid-low-level-calls, avoid-call-value - (bool success, ) = recipient.call{ value: amount }(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain`call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { - return _functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - return _functionCallWithValue(target, data, value, errorMessage); - } - - function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { - require(isContract(target), "Address: call to non-contract"); - - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - - // solhint-disable-next-line no-inline-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -pragma solidity 0.6.9; - -/** - * @dev Implementation of the {IERC20} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * For a generic mechanism see {ERC20PresetMinterPauser}. - * - * TIP: For a detailed writeup see our guide - * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. - * - * We have followed general OpenZeppelin guidelines: functions revert instead - * of returning `false` on failure. This behavior is nonetheless conventional - * and does not conflict with the expectations of ERC20 applications. - * - * Additionally, an {Approval} event is emitted on calls to {transferFrom}. - * This allows applications to reconstruct the allowance for all accounts just - * by listening to said events. Other implementations of the EIP may not emit - * these events, as it isn't required by the specification. - * - * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} - * functions have been added to mitigate the well-known issues around setting - * allowances. See {IERC20-approve}. - */ -contract ERC20 is Context, IERC20 { - using SafeMath for uint256; - using Address for address; - - bool cantransfer; - uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; + bool cantransfer; + address govAddr; + address helperAddr; + IERC20 dodo; - mapping (address => uint256) private _balances; - mapping (address => mapping (address => uint256)) private _allowances; + + uint256 public alpha = 100; + uint256 public totalVdodoAmount; + uint256 public totalOverdraft; + + uint256 public dodoPerBlock = 1e18;//TODO + uint256 public lastRewardBlock ; + uint256 public dodoFeeDestroyRatio ; + uint256 constant public _MAG_SP_AMOUNT_ = 10; + uint256 constant public _MAG_TOTALSP_AMOUNT_ = 110; + uint256 constant public _BASE_AMOUNT_ = 100; + address constant public _DESTROY_ADDRESS_ = 0x0000000000000000000000000000000000000000; + + uint256 constant public _MIN_X_ = 1; + uint256 constant public _MIN_X_Y_ = 5; + uint256 constant public _MAX_X_ = 10; + uint256 constant public _MAX_X_Y_ = 15; + + mapping(address => mapping(address => uint256)) internal _ALLOWED_; mapping(address => bool) public operater; + mapping(address => UserInfo) public userInfo; + + struct UserInfo { + address superior; + uint256 vdodoAmount; + uint256 overdraft; + uint256 totalRedeem; + bool hasParticipateGov; //是否正在参与治理,是的话就不可以提币 + } + + + // ============ Events ============ + event ParticipatoryGov(address indexed user, uint256 amount); + event Deposit(address indexed user,address indexed superior, uint256 amount); + event Redeem(address indexed user, uint256 amount); + event SetCantransfer(bool allowed); + event RemoveOperation(address indexed operater); + event AddOperation(address indexed operater); + event ChangePerReward(uint256 dodoPerBlock); + event UpdateDodoFeeDestroyRatio(uint256 dodoFeeDestroyRatio); + event Transfer(address indexed from, address indexed to, uint256 amount); + event Approval(address indexed owner, address indexed spender, uint256 amount); // ============ Modifiers ============ modifier onlyOperater() { require(cantransfer || operater[msg.sender] , "not allowed transfer"); _; } - /** - * @dev Sets the values for {name} and {symbol}, initializes {decimals} with - * a default value of 18. - * - * To select a different value for {decimals}, use {_setupDecimals}. - * - * All three of these values are immutable: they can only be set once during - * construction. - */ - constructor (string memory name, string memory symbol) public { - _name = name; - _symbol = symbol; - _decimals = 18; - } - - /** - * @dev Returns the name of the token. - */ - function name() public view returns (string memory) { - return _name; - } - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() public view returns (string memory) { - return _symbol; - } - - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5,05` (`505 / 10 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is - * called. - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. - */ - function decimals() public view returns (uint8) { - return _decimals; - } - - /** - * @dev See {IERC20-totalSupply}. - */ - function totalSupply() public view override returns (uint256) { - return _totalSupply; - } - - /** - * @dev See {IERC20-balanceOf}. - */ - function balanceOf(address account) public view virtual override returns (uint256) { - return _balances[account]; - } - - /** - * @dev See {IERC20-transfer}. - * - * Requirements: - * - * - `recipient` cannot be the zero address. - * - the caller must have a balance of at least `amount`. - */ - function transfer(address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(_msgSender(), recipient, amount); - return true; - } - - /** - * @dev See {IERC20-allowance}. - */ - function allowance(address owner, address spender) public view virtual override returns (uint256) { - return _allowances[owner][spender]; - } - - /** - * @dev See {IERC20-approve}. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function approve(address spender, uint256 amount) public virtual override returns (bool) { - _approve(_msgSender(), spender, amount); - return true; - } - - /** - * @dev See {IERC20-transferFrom}. - * - * Emits an {Approval} event indicating the updated allowance. This is not - * required by the EIP. See the note at the beginning of {ERC20}; - * - * Requirements: - * - `sender` and `recipient` cannot be the zero address. - * - `sender` must have a balance of at least `amount`. - * - the caller must have allowance for ``sender``'s tokens of at least - * `amount`. - */ - function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(sender, recipient, amount); - _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount)); - return true; - } - - /** - * @dev Atomically increases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { - _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); - return true; - } - - /** - * @dev Atomically decreases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - * - `spender` must have allowance for the caller of at least - * `subtractedValue`. - */ - function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { - _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue)); - return true; - } - - /** - * @dev Moves tokens `amount` from `sender` to `recipient`. - * - * This is internal function is equivalent to {transfer}, and can be used to - * e.g. implement automatic token fees, slashing mechanisms, etc. - * - * Emits a {Transfer} event. - * - * Requirements: - * - * - `sender` cannot be the zero address. - * - `recipient` cannot be the zero address. - * - `sender` must have a balance of at least `amount`. - */ - function _transfer(address sender, address recipient, uint256 amount) internal onlyOperater virtual { - require(sender != address(0), "ERC20: transfer from the zero address"); - require(recipient != address(0), "ERC20: transfer to the zero address"); - - _beforeTokenTransfer(sender, recipient, amount); - - _balances[sender] = _balances[sender].sub(amount); - _balances[recipient] = _balances[recipient].add(amount); - emit Transfer(sender, recipient, amount); - } - - /** @dev Creates `amount` tokens and assigns them to `account`, increasing - * the total supply. - * - * Emits a {Transfer} event with `from` set to the zero address. - * - * Requirements - * - * - `to` cannot be the zero address. - */ - function _mint(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: mint to the zero address"); - - _beforeTokenTransfer(address(0), account, amount); - - _totalSupply = _totalSupply.add(amount); - _balances[account] = _balances[account].add(amount); - emit Transfer(address(0), account, amount); - } - - /** - * @dev Destroys `amount` tokens from `account`, reducing the - * total supply. - * - * Emits a {Transfer} event with `to` set to the zero address. - * - * Requirements - * - * - `account` cannot be the zero address. - * - `account` must have at least `amount` tokens. - */ - function _burn(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: burn from the zero address"); - - _beforeTokenTransfer(account, address(0), amount); - - _balances[account] = _balances[account].sub(amount); - _totalSupply = _totalSupply.sub(amount); - emit Transfer(account, address(0), amount); - } - - /** - * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. - * - * This is internal function is equivalent to `approve`, and can be used to - * e.g. set automatic allowances for certain subsystems, etc. - * - * Emits an {Approval} event. - * - * Requirements: - * - * - `owner` cannot be the zero address. - * - `spender` cannot be the zero address. - */ - function _approve(address owner, address spender, uint256 amount) internal onlyOperater virtual { - require(owner != address(0), "ERC20: approve from the zero address"); - require(spender != address(0), "ERC20: approve to the zero address"); - - _allowances[owner][spender] = amount; - emit Approval(owner, spender, amount); - } - - /** - * @dev Sets {decimals} to a value other than the default one of 18. - * - * WARNING: This function should only be called from the constructor. Most - * applications that interact with token contracts will not expect - * {decimals} to ever change, and may work incorrectly if it does. - */ - function _setupDecimals(uint8 decimals_) internal { - _decimals = decimals_; - } - - function _setCantransfer(bool _cantransfer) internal { - cantransfer = _cantransfer; - } - - function _addOperation(address _operater) internal { - operater[_operater] = true; - } - function _removeOperation(address _operater) internal { - operater[_operater] = false; - } - - /** - * @dev Hook that is called before any transfer of tokens. This includes - * minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * will be to transferred to `to`. - * - when `from` is zero, `amount` tokens will be minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens will be burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } -} -pragma solidity 0.6.9; -interface IGovernance { - function governanceCall(address account, uint256 amount,bytes calldata data) external returns (bool); - -} -pragma solidity 0.6.9; -contract SpToken is ERC20("StakingPowerToken", "SPT"), Ownable ,ReentrancyGuard{ - using SafeMath for uint256; - using SafeERC20 for IERC20; - - address govAddr; - IERC20 dodo; - - uint256 public alpha = 1; - uint256 public totalSp; - uint256 public totalOverdraft; - - uint256 public _DODOPERBLOCK_ = 1e18;//TODO - uint256 constant public _MAG_SP_AMOUNT_ = 10; - uint256 constant public _MAG_TOTALSP_AMOUNT_ = 110; - uint256 constant public _BASE_AMOUNT_ = 100; - - struct UserInfo { - uint256 dodoAmount; - address upline; - uint256 spAmount; - uint256 overdraft; - uint256 lastRewardBlock; - uint256 totalRedeem; - bool hasParticipateGov; //是否正在参与治理,是的话就不可以提币 - } - - mapping (address => UserInfo) public userInfo; - -// ============ Events ============ - event ParticipatoryGov(address indexed user, uint256 amount); - event Deposit(address indexed user,address indexed upline, uint256 amount); - event Redeem(address indexed user, uint256 amount); - event SetCantransfer(bool allowed); - event RemoveOperation(address indexed _operater); - event AddOperation(address indexed _operater); - receive() external payable { revert(); } - // ============ Functions ============ + constructor( address _govAddr, - address _dodo - - ) public { + address _dodo, + address _helperAddr, + string memory name, + string memory symbol) + public { + _name = name; + _symbol = symbol; + _decimals = 18; govAddr = _govAddr; + helperAddr = _helperAddr; + dodo = IERC20(_dodo); } - - - function mint(address _to, uint256 _amount) public onlyOwner { - _mint(_to, _amount); + function name() public view override returns (string memory) { + return _name; } - function burn(address _to, uint256 amount) public onlyOwner{ - _burn(_to,amount); + function symbol() public view override returns (string memory) { + return _symbol; } + function decimals() public view override returns (uint8) { + return _decimals; + } + function totalSupply() public view override returns (uint256) { + return totalVdodoAmount; + } + + // ============ Ownable Functions ============ + function setCantransfer(bool _allowed) public onlyOwner { - _setCantransfer(_allowed); + cantransfer = _allowed; emit SetCantransfer(_allowed); } - function addOperationAddress(address _operationAddress) public onlyOwner { - _addOperation(_operationAddress); - emit AddOperation(_operationAddress); + function addOperationAddress(address _operater) public onlyOwner { + operater[_operater] = true; + emit AddOperation(_operater); } - function removeOperation(address _operationAddress) public onlyOwner { - _removeOperation(_operationAddress); - emit RemoveOperation(_operationAddress); + function removeOperation(address _operater) public onlyOwner { + operater[_operater] = false; + emit RemoveOperation(_operater); } + function changePerReward(uint256 _dodoPerBlock) public onlyOwner { + dodoPerBlock = _dodoPerBlock; + emit ChangePerReward(dodoPerBlock); + } + function updateDodoFeeDestroyRatio(uint256 _dodoFeeDestroyRatio) public onlyOwner { + dodoFeeDestroyRatio = _dodoFeeDestroyRatio; + emit UpdateDodoFeeDestroyRatio(_dodoFeeDestroyRatio); + } + + + // ============ Functions ============ + + //TODO 投票与代理是否分开 function participatoryGov( uint256 _amount, bytes calldata _data ) external preventReentrant { - UserInfo storage user = userInfo[msg.sender]; - require(user.spAmount>_amount,"no enough sp"); + UserInfo memory user = userInfo[msg.sender]; + require(user.vdodoAmount>_amount,"no enough vdodo"); if (_data.length > 0) IGovernance(govAddr).governanceCall(msg.sender, _amount, _data); - user.spAmount = user.spAmount.sub(_amount); - user.hasParticipateGov = true; + + uint256 userVdodoAmount = user.vdodoAmount.sub(_amount); + _updateUserData(msg.sender,userVdodoAmount,0); + totalVdodoAmount = totalVdodoAmount.sub(_amount); + _changeUserParticipateState(msg.sender,true); emit ParticipatoryGov(msg.sender, _amount); } - //TODO _uplineAddress??? deposit again???? //TODO round up /down - function deposit(uint256 _amount,address _uplineAddress) public preventReentrant{ + function deposit(uint256 _amount,address _superiorAddress) public preventReentrant{ require(_amount>0,"must deposit greater than 0"); - dodo.transferFrom(msg.sender, address(this), _amount); + _updateAlpha(); - UserInfo storage user = userInfo[msg.sender]; - if(user.dodoAmount==0){ - user.lastRewardBlock = block.number; - } - user.dodoAmount = user.dodoAmount.add(_amount); - // accuDODO = _DODOPERBLOCK_*(block-lastRewardBlock) - uint256 accuDODO = _DODOPERBLOCK_ * (block.number.sub(user.lastRewardBlock)); - //TODO FIRST DEPOSIT??? - if(totalSp > 0){ - // alpha = alpha + accuDODO/totalSp (round down) - alpha = alpha.add(accuDODO.div(totalSp)); - } - + UserInfo memory user = userInfo[msg.sender]; // 自己的sp + x/alpha - uint256 newSpAmount = _amount.div(alpha); + uint256 newVdodoAmount = _amount.div(alpha); + uint256 fromVdodoAmount = user.vdodoAmount.add(newVdodoAmount); + _updateUserData(msg.sender,fromVdodoAmount,0); - _mint(msg.sender,newSpAmount); - // spToken.mint(msg.sender,newSpAmount); - - user.spAmount = user.spAmount.add(newSpAmount); - if(user.upline==address(0x0)){ - user.upline = _uplineAddress; + if(user.superior==address(0x0) && _superiorAddress != address(0x0)){ + _updateSuperiorAddress(msg.sender,_superiorAddress); } - UserInfo storage uplineUser = userInfo[user.upline]; + + UserInfo memory superiorUser = userInfo[user.superior]; // 上级sp +( x/alpha)* 0.1 (round up) - uplineUser.spAmount = uplineUser.spAmount.add( - _amount.mul(alpha) + uint256 superiorVdodoAmount = superiorUser.vdodoAmount.add( + _amount.div(alpha) .mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); // 上级DODO欠款 + x*0.1 (round up) uint256 overdraft = _amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_); - uplineUser.overdraft = uplineUser.overdraft.add(overdraft); + uint256 superiorOverdraft = superiorUser.overdraft.add(overdraft); - totalOverdraft = totalOverdraft.add(overdraft); + _updateUserData(user.superior,superiorVdodoAmount,superiorOverdraft); + + uint256 newTotalOverdraft = totalOverdraft.add(overdraft); + _updateTotalOverdraft(newTotalOverdraft); // total sp + x/alpha*1.1 - totalSp = totalSp.add(_amount.div(alpha).mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); - emit Deposit(msg.sender,_uplineAddress, _amount); + uint256 newTotalVdodoAmount = totalVdodoAmount.add(_amount.div(alpha).mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); + _updateTotalVdodoAmount(newTotalVdodoAmount); + emit Deposit(msg.sender,_superiorAddress, _amount); } + //TODO round up /down - function redeem(uint256 _amount) public preventReentrant{ - UserInfo storage user = userInfo[msg.sender]; - require(user.spAmount>_amount,"no enough sp token"); + UserInfo memory user = userInfo[msg.sender]; + require(user.vdodoAmount>=_amount,"no enough vdodo token"); require(!user.hasParticipateGov,"hasParticipateGov"); + _updateAlpha(); - - // accuDODO = _DODOPERBLOCK_*(block-lastRewardBlock) - uint256 accuDODO = _DODOPERBLOCK_ * (block.number.sub(user.lastRewardBlock)); - // alpha = alpha + accuDODO/totalSp (round down) - alpha = alpha.add(accuDODO.div(totalSp)); // 自己的sp - x - _burn(msg.sender,_amount); - // spToken.burn(msg.sender,_amount); - user.spAmount = user.spAmount.sub(_amount); + uint256 userVdodoAmount = user.vdodoAmount.sub(_amount); + _updateUserData(msg.sender,userVdodoAmount,0); - UserInfo storage uplineUser = userInfo[user.upline]; + UserInfo memory superiorUser = userInfo[user.superior]; // 上级sp - (x)*0.1(round down) - uplineUser.spAmount = uplineUser.spAmount.sub( + uint256 superiorVdodoAmount = superiorUser.vdodoAmount.sub( _amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); // 上级DODO欠款 - x*alpha*0.1 (round down) uint256 overdraft = _amount.mul(alpha).mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_); - uplineUser.overdraft = uplineUser.overdraft.sub(overdraft); - - totalOverdraft = totalOverdraft.sub(overdraft); - - // total sp - x*1.1 - totalSp = totalSp.sub(_amount.mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); - - user.lastRewardBlock = block.number; - - uint256 feeRatio = checkReward(_amount); - - // x * 80% transfer to user - uint256 receiveAmount = _amount.mul(_BASE_AMOUNT_.sub(feeRatio).div(_BASE_AMOUNT_)); - - dodo.safeTransferFrom(address(this), msg.sender, receiveAmount); - user.dodoAmount = user.dodoAmount.sub(receiveAmount); - user.totalRedeem = user.totalRedeem.add(receiveAmount); - - // alpha = alpha + x * 20% /totalSp - uint256 feeAmount = _amount.mul(feeRatio.div(_BASE_AMOUNT_)); - alpha = alpha.add(feeAmount.div(totalSp)); - - //TODO 3. 这部分税会继续拆成两部分,第一部分销毁,第二部分分给所有vDODO持有人 + uint256 superiorOverdraft= superiorUser.overdraft.sub(overdraft); + _updateUserData(user.superior,superiorVdodoAmount,superiorOverdraft); + uint256 newTotalOverdraft = totalOverdraft.sub(overdraft); + _updateTotalOverdraft(newTotalOverdraft); + + // total sp - (x+x*0.1)//TODO + uint256 newTotalVdodoAmount = totalVdodoAmount.sub(_amount.mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); + _updateTotalVdodoAmount(newTotalVdodoAmount); + + lastRewardBlock = block.number; + + uint256 feeRatio = checkReward(); + // alpha* x * 80% transfer to user + uint256 dodoAmount = alpha.mul(_amount); + + uint256 dodoFee = dodoAmount.mul(feeRatio).div(_BASE_AMOUNT_); + uint256 dodoReceive = dodoAmount.sub(dodoFee); + + dodo.safeTransferFrom(address(this), msg.sender, dodoReceive); + uint256 newRedeem = user.totalRedeem.add(dodoReceive); + _updateUserRedeem(msg.sender,newRedeem); + + // 3. 这部分税会继续拆成两部分,第一部分销毁,第二部分分给所有vDODO持有人 + uint256 distributeAmount = dodoFee; + //是否需要销毁 + if(dodoFeeDestroyRatio>0){ + // uint256 dodoFee = dodoAmount.mul(feeRatio).div(_BASE_AMOUNT_); + uint256 destroyAmount = dodoFee.mul(dodoFeeDestroyRatio).div(_BASE_AMOUNT_); + transfer(_DESTROY_ADDRESS_, destroyAmount); + distributeAmount = dodoFee.sub(destroyAmount); + } + + //可以设置 + + // alpha = alpha*X + x * 20% /totalSp + uint256 feeAmount = _amount.mul(distributeAmount).div(_BASE_AMOUNT_); + + alpha = alpha.mul(_amount).add(feeAmount.div(totalVdodoAmount)); emit Redeem(msg.sender, _amount); } - - //TODO - function checkReward(uint256 _amount) internal returns(uint256) { - - // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 (5≤ y ≤ 15) - - // y = 5 (x ≤ 1) - - // y = 15 (x ≥ 10) - } // balanceOf = sp-DODO欠款/alpha function balanceOf(address _address) public view override returns (uint256 balance) { UserInfo memory user = userInfo[_address]; - balance = user.spAmount.sub(user.overdraft.div(alpha)); + balance = user.vdodoAmount.sub(user.overdraft.div(alpha)); } function transfer(address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(_msgSender(), recipient, amount); + _transfer(msg.sender, recipient, amount); return true; } - function _transfer(address sender, address recipient, uint256 _amount) internal onlyOperater virtual override { - require(sender != address(0), " transfer from the zero address"); - require(recipient != address(0), " transfer to the zero address"); + function approve(address spender, uint256 amount) public virtual override returns (bool) { + _approve(msg.sender, spender, amount); + return true; + } + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, msg.sender, _ALLOWED_[sender][msg.sender].sub(amount)); + return true; + } + function _approve( + address owner, + address spender, + uint256 amount + ) private onlyOperater { + _ALLOWED_[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + function allowance(address owner, address spender) public view override returns (uint256) { + return _ALLOWED_[owner][spender]; + } + + function _transfer(address from, address to, uint256 _amount) internal onlyOperater virtual { + require(from != address(0), " transfer from the zero address"); + require(to != address(0), " transfer to the zero address"); // require(balanceOf(from)≥amount) - require(balanceOf(sender) >= _amount,"no enough to sp transfer"); - UserInfo storage user = userInfo[sender]; - // sp[msg.sender] -= amount - user.spAmount = user.spAmount.sub(_amount); + require(balanceOf(from) >= _amount,"no enough to transfer"); + UserInfo memory user = userInfo[from]; + // sp[from] -= amount + uint256 fromSpAmount = user.vdodoAmount.sub(_amount); + _updateUserData(from,fromSpAmount,0); // sp[上级[from]] -= amount*0.1 (round down) - UserInfo storage uplineUser = userInfo[user.upline]; - uplineUser.spAmount = uplineUser.spAmount.sub(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); + UserInfo memory fromSuperior = userInfo[user.superior]; + uint256 fromSuperiorSpAmount = fromSuperior.vdodoAmount.sub(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); + _updateUserData(user.superior,fromSuperiorSpAmount,0); - UserInfo storage recipientUser = userInfo[recipient]; + UserInfo memory toUser = userInfo[to]; // sp[to] += amount - recipientUser.spAmount = recipientUser.spAmount.add(_amount); + uint256 toSpAmount = toUser.vdodoAmount.add(_amount); + _updateUserData(to,toSpAmount,0); - UserInfo storage recipientUplineUser = userInfo[recipientUser.upline]; - recipientUplineUser.spAmount =recipientUplineUser.spAmount.add(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); - emit Transfer(sender, recipient, _amount); + // sp[上级[to]] += amount*0.1 + UserInfo memory toSuperior = userInfo[toUser.superior]; + uint256 toSuperiorSpAmount =toSuperior.vdodoAmount.add(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); + _updateUserData(toUser.superior,toSuperiorSpAmount,0); + + emit Transfer(from, to, _amount); } // 可提取DODO数额 = sp*alpha - DODO欠款 function canWithDraw(address _address) public view returns (uint256 withDrawAmount) { UserInfo memory user = userInfo[_address]; - withDrawAmount = user.spAmount.mul(alpha).sub(user.overdraft); + withDrawAmount = user.vdodoAmount.mul(alpha).sub(user.overdraft); } - function checkUserInfo(address _userAddress) public view returns(uint256,address,uint256,uint256,uint256,uint256,bool) { + function checkUserInfo(address _userAddress) public view returns(address,uint256,uint256,uint256,bool) { UserInfo memory user = userInfo[_userAddress]; - return (user.dodoAmount, user.upline, user.spAmount, user.overdraft,user.lastRewardBlock,user.totalRedeem,user.hasParticipateGov); + return (user.superior, user.vdodoAmount, user.overdraft,user.totalRedeem,user.hasParticipateGov); } + // ============ internal function ============ + function _updateAlpha() internal { + // accuDODO = dodoPerBlock*(block-lastRewardBlock) + uint256 accuDODO = dodoPerBlock * (block.number.sub(lastRewardBlock)); + if(totalVdodoAmount > 0){ + // alpha = alpha + accuDODO/totalSp (round down) + alpha = alpha.add(accuDODO.div(totalVdodoAmount)); + } + } + function _updateUserData(address _who,uint256 _vdodoAmount,uint256 _overdraft) internal { + UserInfo storage userWho = userInfo[_who]; + if(_vdodoAmount>0){ + userWho.vdodoAmount = _vdodoAmount; + } + if(_overdraft>0){ + userWho.overdraft = _overdraft; + } + } + function _updateUserRedeem(address _who,uint256 _newRedeem) internal { + if(_newRedeem>0){ + UserInfo storage userWho = userInfo[_who]; + userWho.totalRedeem = _newRedeem; + } + } + function _updateSuperiorAddress(address _who,address _newAddres) internal { + UserInfo storage userWho = userInfo[_who]; + userWho.superior = _newAddres; + } + function _changeUserParticipateState(address _who,bool _newState) internal { + UserInfo storage userWho = userInfo[_who]; + userWho.hasParticipateGov = _newState; + } + + function _updateTotalOverdraft(uint256 _overdraft) internal { + totalOverdraft = _overdraft; + } + + function _updateTotalVdodoAmount(uint256 _newVdodoAmount) internal { + totalVdodoAmount = _newVdodoAmount; + } + // ============= Helper and calculation function =============== + function checkReward() internal returns(uint256) { + uint256 dodoTotalAmout = IHelper(helperAddr).getDodoAmount(); + // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 ==> y = sqrt(100* (x*x +2x ) / 81)) +15 + // y = 5 (x ≤ 1) + // y = 15 (x ≥ 10) + uint256 x = dodoTotalAmout.divCeil(totalVdodoAmount); + if(x<=_MIN_X_){ + return _MIN_X_Y_; + }else if(x>=_MAX_X_){ + return _MAX_X_Y_; + }else{ + uint256 rewardAmount = x.mul(x).add(x.mul(2)).mul(100).div(81).sqrt().add(15); + return rewardAmount; + } + } } -// deposit 是否需要输入上级地址 -// round up & round down -//vDODO的分红 -// + +//官方的收益会定期回购DODO Token并分红。因此要留一个donate接口,方便外部注入资金----> 为什么不可以使用 DODOToken.transfer? + +// TODO DecimalMath calculation \ No newline at end of file From c21d8331c6190bc35e4153fd0a61d20105ca92c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Sun, 31 Jan 2021 16:22:44 +0800 Subject: [PATCH 04/42] Revert "Update VDODOChef.sol" This reverts commit 7c8b443d2b4a521a6c5b61a70adebe940b705a4e. --- contracts/VDODO/VDODOChef.sol | 1224 +++++++++++++++++++++++++-------- 1 file changed, 943 insertions(+), 281 deletions(-) diff --git a/contracts/VDODO/VDODOChef.sol b/contracts/VDODO/VDODOChef.sol index fbc80e0..fda7398 100644 --- a/contracts/VDODO/VDODOChef.sol +++ b/contracts/VDODO/VDODOChef.sol @@ -4,392 +4,1054 @@ SPDX-License-Identifier: Apache-2.0 */ -import {IERC20} from "../intf/IERC20.sol"; -import {Address} from "../lib/Address.sol"; -import {SafeMath} from "../lib/SafeMath.sol"; -import {DecimalMath} from "../lib/DecimalMath.sol"; -import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; -import {SafeERC20} from "../lib/SafeERC20.sol"; -import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; pragma solidity 0.6.9; -interface IGovernance { - function governanceCall(address account, uint256 amount,bytes calldata data) external returns (bool); +pragma experimental ABIEncoderV2; + +/** + * @title SafeMath + * @author DODO Breeder + * + * @notice Math operations with safety checks that revert on error + */ +library SafeMath { + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, "MUL_ERROR"); + + return c; + } + + function div(uint256 a, uint256 b) internal pure returns (uint256) { + require(b > 0, "DIVIDING_ERROR"); + return a / b; + } + + function divCeil(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 quotient = div(a, b); + uint256 remainder = a - quotient * b; + if (remainder > 0) { + return quotient + 1; + } else { + return quotient; + } + } + + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + require(b <= a, "SUB_ERROR"); + return a - b; + } + + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, "ADD_ERROR"); + return c; + } + + function sqrt(uint256 x) internal pure returns (uint256 y) { + uint256 z = x / 2 + 1; + y = x; + while (z < y) { + y = z; + z = (x / z + z) / 2; + } + } } pragma solidity 0.6.9; -interface IHelper { - function getDodoAmount() external returns (uint256); -} -pragma solidity 0.6.9; -contract SpToken is IERC20,InitializableOwnable ,ReentrancyGuard{ +/** + * @title DecimalMath + * @author DODO Breeder + * + * @notice Functions for fixed point number with 18 decimals + */ +library DecimalMath { using SafeMath for uint256; - using SafeERC20 for IERC20; + uint256 internal constant ONE = 10**18; + uint256 internal constant ONE2 = 10**36; + + function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) { + return target.mul(d) / (10**18); + } + + function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) { + return target.mul(d).divCeil(10**18); + } + + function divFloor(uint256 target, uint256 d) internal pure returns (uint256) { + return target.mul(10**18).div(d); + } + + function divCeil(uint256 target, uint256 d) internal pure returns (uint256) { + return target.mul(10**18).divCeil(d); + } + + function reciprocalFloor(uint256 target) internal pure returns (uint256) { + return uint256(10**36).div(target); + } + + function reciprocalCeil(uint256 target) internal pure returns (uint256) { + return uint256(10**36).divCeil(target); + } +} +// File: @openzeppelin/contracts/access/Ownable.sol + + + +pragma solidity 0.6.9; +/** + * @title Ownable + * @author DODO Breeder + * + * @notice Ownership related functions + */ +contract Ownable { + address public _OWNER_; + address public _NEW_OWNER_; + + // ============ Events ============ + + event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner); + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + // ============ Modifiers ============ + + modifier onlyOwner() { + require(msg.sender == _OWNER_, "NOT_OWNER"); + _; + } + + // ============ Functions ============ + + constructor() internal { + _OWNER_ = msg.sender; + emit OwnershipTransferred(address(0), _OWNER_); + } + + function transferOwnership(address newOwner) external onlyOwner { + emit OwnershipTransferPrepared(_OWNER_, newOwner); + _NEW_OWNER_ = newOwner; + } + + function claimOwnership() external { + require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM"); + emit OwnershipTransferred(_OWNER_, _NEW_OWNER_); + _OWNER_ = _NEW_OWNER_; + _NEW_OWNER_ = address(0); + } +} + + +pragma solidity 0.6.9; + +/** + * @title ReentrancyGuard + * @author DODO Breeder + * + * @notice Protect functions from Reentrancy Attack + */ +contract ReentrancyGuard { + // https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=zero-state#scoping-and-declarations + // zero-state of _ENTERED_ is false + bool private _ENTERED_; + + modifier preventReentrant() { + require(!_ENTERED_, "REENTRANT"); + _ENTERED_ = true; + _; + _ENTERED_ = false; + } +} + +// File: @openzeppelin/contracts/token/ERC20/IERC20.sol + + + +pragma solidity 0.6.9; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +pragma solidity 0.6.9; + +/* + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with GSN meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address payable) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes memory) { + this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 + return msg.data; + } +} + +pragma solidity 0.6.9; +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using SafeMath for uint256; + + function safeTransfer( + IERC20 token, + address to, + uint256 value + ) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom( + IERC20 token, + address from, + address to, + uint256 value + ) internal { + _callOptionalReturn( + token, + abi.encodeWithSelector(token.transferFrom.selector, from, to, value) + ); + } + + function safeApprove( + IERC20 token, + address spender, + uint256 value + ) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + // solhint-disable-next-line max-line-length + require( + (value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. + + // A Solidity high level call has three parts: + // 1. The target address is checked to verify it contains contract code + // 2. The call itself is made, and success asserted + // 3. The return value is decoded, which in turn checks the size of the returned data. + // solhint-disable-next-line max-line-length + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = address(token).call(data); + require(success, "SafeERC20: low-level call failed"); + + if (returndata.length > 0) { + // Return data is optional + // solhint-disable-next-line max-line-length + require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); + } + } +} + +// File: @openzeppelin/contracts/utils/Address.sol +pragma solidity 0.6.9; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // According to EIP-1052, 0x0 is the value returned for not-yet created accounts + // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned + // for accounts without code, i.e. `keccak256('')` + bytes32 codehash; + bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; + // solhint-disable-next-line no-inline-assembly + assembly { codehash := extcodehash(account) } + return (codehash != accountHash && codehash != 0x0); + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{ value: amount }(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain`call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + return _functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + return _functionCallWithValue(target, data, value, errorMessage); + } + + function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { + require(isContract(target), "Address: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} + +pragma solidity 0.6.9; + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20PresetMinterPauser}. + * + * TIP: For a detailed writeup see our guide + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * We have followed general OpenZeppelin guidelines: functions revert instead + * of returning `false` on failure. This behavior is nonetheless conventional + * and does not conflict with the expectations of ERC20 applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + */ +contract ERC20 is Context, IERC20 { + using SafeMath for uint256; + using Address for address; + + bool cantransfer; + uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; - bool cantransfer; - address govAddr; - address helperAddr; - IERC20 dodo; - - uint256 public alpha = 100; - uint256 public totalVdodoAmount; - uint256 public totalOverdraft; - - uint256 public dodoPerBlock = 1e18;//TODO - uint256 public lastRewardBlock ; - uint256 public dodoFeeDestroyRatio ; - uint256 constant public _MAG_SP_AMOUNT_ = 10; - uint256 constant public _MAG_TOTALSP_AMOUNT_ = 110; - uint256 constant public _BASE_AMOUNT_ = 100; - address constant public _DESTROY_ADDRESS_ = 0x0000000000000000000000000000000000000000; - - uint256 constant public _MIN_X_ = 1; - uint256 constant public _MIN_X_Y_ = 5; - uint256 constant public _MAX_X_ = 10; - uint256 constant public _MAX_X_Y_ = 15; - - mapping(address => mapping(address => uint256)) internal _ALLOWED_; + mapping (address => uint256) private _balances; + mapping (address => mapping (address => uint256)) private _allowances; mapping(address => bool) public operater; - mapping(address => UserInfo) public userInfo; - - struct UserInfo { - address superior; - uint256 vdodoAmount; - uint256 overdraft; - uint256 totalRedeem; - bool hasParticipateGov; //是否正在参与治理,是的话就不可以提币 - } - - - // ============ Events ============ - event ParticipatoryGov(address indexed user, uint256 amount); - event Deposit(address indexed user,address indexed superior, uint256 amount); - event Redeem(address indexed user, uint256 amount); - event SetCantransfer(bool allowed); - event RemoveOperation(address indexed operater); - event AddOperation(address indexed operater); - event ChangePerReward(uint256 dodoPerBlock); - event UpdateDodoFeeDestroyRatio(uint256 dodoFeeDestroyRatio); - event Transfer(address indexed from, address indexed to, uint256 amount); - event Approval(address indexed owner, address indexed spender, uint256 amount); // ============ Modifiers ============ modifier onlyOperater() { require(cantransfer || operater[msg.sender] , "not allowed transfer"); _; } - receive() external payable { - revert(); - } - - constructor( - address _govAddr, - address _dodo, - address _helperAddr, - string memory name, - string memory symbol) - public { + /** + * @dev Sets the values for {name} and {symbol}, initializes {decimals} with + * a default value of 18. + * + * To select a different value for {decimals}, use {_setupDecimals}. + * + * All three of these values are immutable: they can only be set once during + * construction. + */ + constructor (string memory name, string memory symbol) public { _name = name; _symbol = symbol; _decimals = 18; - govAddr = _govAddr; - helperAddr = _helperAddr; - - dodo = IERC20(_dodo); } - function name() public view override returns (string memory) { + + /** + * @dev Returns the name of the token. + */ + function name() public view returns (string memory) { return _name; } - function symbol() public view override returns (string memory) { + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view returns (string memory) { return _symbol; } - function decimals() public view override returns (uint8) { + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5,05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is + * called. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view returns (uint8) { return _decimals; } + + /** + * @dev See {IERC20-totalSupply}. + */ function totalSupply() public view override returns (uint256) { - return totalVdodoAmount; + return _totalSupply; } - // ============ Ownable Functions ============ + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual override returns (uint256) { + return _balances[account]; + } + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `recipient` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(_msgSender(), recipient, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + _approve(_msgSender(), spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}; + * + * Requirements: + * - `sender` and `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + * - the caller must have allowance for ``sender``'s tokens of at least + * `amount`. + */ + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount)); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue)); + return true; + } + + /** + * @dev Moves tokens `amount` from `sender` to `recipient`. + * + * This is internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `sender` cannot be the zero address. + * - `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + */ + function _transfer(address sender, address recipient, uint256 amount) internal onlyOperater virtual { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount); + _balances[recipient] = _balances[recipient].add(amount); + emit Transfer(sender, recipient, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements + * + * - `to` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + _balances[account] = _balances[account].sub(amount); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. + * + * This is internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve(address owner, address spender, uint256 amount) internal onlyOperater virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + /** + * @dev Sets {decimals} to a value other than the default one of 18. + * + * WARNING: This function should only be called from the constructor. Most + * applications that interact with token contracts will not expect + * {decimals} to ever change, and may work incorrectly if it does. + */ + function _setupDecimals(uint8 decimals_) internal { + _decimals = decimals_; + } + + function _setCantransfer(bool _cantransfer) internal { + cantransfer = _cantransfer; + } + + function _addOperation(address _operater) internal { + operater[_operater] = true; + } + function _removeOperation(address _operater) internal { + operater[_operater] = false; + } + + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be to transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } +} +pragma solidity 0.6.9; +interface IGovernance { + function governanceCall(address account, uint256 amount,bytes calldata data) external returns (bool); + +} +pragma solidity 0.6.9; +contract SpToken is ERC20("StakingPowerToken", "SPT"), Ownable ,ReentrancyGuard{ + using SafeMath for uint256; + using SafeERC20 for IERC20; + + address govAddr; + IERC20 dodo; + + uint256 public alpha = 1; + uint256 public totalSp; + uint256 public totalOverdraft; + + uint256 public _DODOPERBLOCK_ = 1e18;//TODO + uint256 constant public _MAG_SP_AMOUNT_ = 10; + uint256 constant public _MAG_TOTALSP_AMOUNT_ = 110; + uint256 constant public _BASE_AMOUNT_ = 100; + + struct UserInfo { + uint256 dodoAmount; + address upline; + uint256 spAmount; + uint256 overdraft; + uint256 lastRewardBlock; + uint256 totalRedeem; + bool hasParticipateGov; //是否正在参与治理,是的话就不可以提币 + } + + mapping (address => UserInfo) public userInfo; + +// ============ Events ============ + event ParticipatoryGov(address indexed user, uint256 amount); + event Deposit(address indexed user,address indexed upline, uint256 amount); + event Redeem(address indexed user, uint256 amount); + event SetCantransfer(bool allowed); + event RemoveOperation(address indexed _operater); + event AddOperation(address indexed _operater); + + receive() external payable { + revert(); + } + // ============ Functions ============ + constructor( + address _govAddr, + address _dodo + + ) public { + govAddr = _govAddr; + dodo = IERC20(_dodo); + } + + + function mint(address _to, uint256 _amount) public onlyOwner { + _mint(_to, _amount); + } + function burn(address _to, uint256 amount) public onlyOwner{ + _burn(_to,amount); + } function setCantransfer(bool _allowed) public onlyOwner { - cantransfer = _allowed; + _setCantransfer(_allowed); emit SetCantransfer(_allowed); } - function addOperationAddress(address _operater) public onlyOwner { - operater[_operater] = true; - emit AddOperation(_operater); + function addOperationAddress(address _operationAddress) public onlyOwner { + _addOperation(_operationAddress); + emit AddOperation(_operationAddress); } - function removeOperation(address _operater) public onlyOwner { - operater[_operater] = false; - emit RemoveOperation(_operater); + function removeOperation(address _operationAddress) public onlyOwner { + _removeOperation(_operationAddress); + emit RemoveOperation(_operationAddress); } - function changePerReward(uint256 _dodoPerBlock) public onlyOwner { - dodoPerBlock = _dodoPerBlock; - emit ChangePerReward(dodoPerBlock); - } - function updateDodoFeeDestroyRatio(uint256 _dodoFeeDestroyRatio) public onlyOwner { - dodoFeeDestroyRatio = _dodoFeeDestroyRatio; - emit UpdateDodoFeeDestroyRatio(_dodoFeeDestroyRatio); - } - - - // ============ Functions ============ - - //TODO 投票与代理是否分开 function participatoryGov( uint256 _amount, bytes calldata _data ) external preventReentrant { - UserInfo memory user = userInfo[msg.sender]; - require(user.vdodoAmount>_amount,"no enough vdodo"); + UserInfo storage user = userInfo[msg.sender]; + require(user.spAmount>_amount,"no enough sp"); if (_data.length > 0) IGovernance(govAddr).governanceCall(msg.sender, _amount, _data); - - uint256 userVdodoAmount = user.vdodoAmount.sub(_amount); - _updateUserData(msg.sender,userVdodoAmount,0); - totalVdodoAmount = totalVdodoAmount.sub(_amount); - _changeUserParticipateState(msg.sender,true); + user.spAmount = user.spAmount.sub(_amount); + user.hasParticipateGov = true; emit ParticipatoryGov(msg.sender, _amount); } + //TODO _uplineAddress??? deposit again???? //TODO round up /down - function deposit(uint256 _amount,address _superiorAddress) public preventReentrant{ + function deposit(uint256 _amount,address _uplineAddress) public preventReentrant{ require(_amount>0,"must deposit greater than 0"); + dodo.transferFrom(msg.sender, address(this), _amount); - _updateAlpha(); - UserInfo memory user = userInfo[msg.sender]; - // 自己的sp + x/alpha - uint256 newVdodoAmount = _amount.div(alpha); - uint256 fromVdodoAmount = user.vdodoAmount.add(newVdodoAmount); - _updateUserData(msg.sender,fromVdodoAmount,0); - - if(user.superior==address(0x0) && _superiorAddress != address(0x0)){ - _updateSuperiorAddress(msg.sender,_superiorAddress); + UserInfo storage user = userInfo[msg.sender]; + if(user.dodoAmount==0){ + user.lastRewardBlock = block.number; } + user.dodoAmount = user.dodoAmount.add(_amount); + // accuDODO = _DODOPERBLOCK_*(block-lastRewardBlock) + uint256 accuDODO = _DODOPERBLOCK_ * (block.number.sub(user.lastRewardBlock)); + //TODO FIRST DEPOSIT??? + if(totalSp > 0){ + // alpha = alpha + accuDODO/totalSp (round down) + alpha = alpha.add(accuDODO.div(totalSp)); + } + + // 自己的sp + x/alpha + uint256 newSpAmount = _amount.div(alpha); - UserInfo memory superiorUser = userInfo[user.superior]; + _mint(msg.sender,newSpAmount); + // spToken.mint(msg.sender,newSpAmount); + + user.spAmount = user.spAmount.add(newSpAmount); + if(user.upline==address(0x0)){ + user.upline = _uplineAddress; + } + UserInfo storage uplineUser = userInfo[user.upline]; // 上级sp +( x/alpha)* 0.1 (round up) - uint256 superiorVdodoAmount = superiorUser.vdodoAmount.add( - _amount.div(alpha) + uplineUser.spAmount = uplineUser.spAmount.add( + _amount.mul(alpha) .mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); // 上级DODO欠款 + x*0.1 (round up) uint256 overdraft = _amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_); - uint256 superiorOverdraft = superiorUser.overdraft.add(overdraft); + uplineUser.overdraft = uplineUser.overdraft.add(overdraft); - _updateUserData(user.superior,superiorVdodoAmount,superiorOverdraft); - - uint256 newTotalOverdraft = totalOverdraft.add(overdraft); - _updateTotalOverdraft(newTotalOverdraft); + totalOverdraft = totalOverdraft.add(overdraft); // total sp + x/alpha*1.1 - uint256 newTotalVdodoAmount = totalVdodoAmount.add(_amount.div(alpha).mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); - _updateTotalVdodoAmount(newTotalVdodoAmount); - emit Deposit(msg.sender,_superiorAddress, _amount); + totalSp = totalSp.add(_amount.div(alpha).mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); + emit Deposit(msg.sender,_uplineAddress, _amount); } - //TODO round up /down + function redeem(uint256 _amount) public preventReentrant{ - UserInfo memory user = userInfo[msg.sender]; - require(user.vdodoAmount>=_amount,"no enough vdodo token"); + UserInfo storage user = userInfo[msg.sender]; + require(user.spAmount>_amount,"no enough sp token"); require(!user.hasParticipateGov,"hasParticipateGov"); - _updateAlpha(); + + // accuDODO = _DODOPERBLOCK_*(block-lastRewardBlock) + uint256 accuDODO = _DODOPERBLOCK_ * (block.number.sub(user.lastRewardBlock)); + // alpha = alpha + accuDODO/totalSp (round down) + alpha = alpha.add(accuDODO.div(totalSp)); // 自己的sp - x + _burn(msg.sender,_amount); + // spToken.burn(msg.sender,_amount); - uint256 userVdodoAmount = user.vdodoAmount.sub(_amount); - _updateUserData(msg.sender,userVdodoAmount,0); + user.spAmount = user.spAmount.sub(_amount); - UserInfo memory superiorUser = userInfo[user.superior]; + UserInfo storage uplineUser = userInfo[user.upline]; // 上级sp - (x)*0.1(round down) - uint256 superiorVdodoAmount = superiorUser.vdodoAmount.sub( + uplineUser.spAmount = uplineUser.spAmount.sub( _amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); // 上级DODO欠款 - x*alpha*0.1 (round down) uint256 overdraft = _amount.mul(alpha).mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_); - uint256 superiorOverdraft= superiorUser.overdraft.sub(overdraft); - _updateUserData(user.superior,superiorVdodoAmount,superiorOverdraft); + uplineUser.overdraft = uplineUser.overdraft.sub(overdraft); + + totalOverdraft = totalOverdraft.sub(overdraft); + + // total sp - x*1.1 + totalSp = totalSp.sub(_amount.mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); + + user.lastRewardBlock = block.number; + + uint256 feeRatio = checkReward(_amount); + + // x * 80% transfer to user + uint256 receiveAmount = _amount.mul(_BASE_AMOUNT_.sub(feeRatio).div(_BASE_AMOUNT_)); + + dodo.safeTransferFrom(address(this), msg.sender, receiveAmount); + user.dodoAmount = user.dodoAmount.sub(receiveAmount); + user.totalRedeem = user.totalRedeem.add(receiveAmount); + + // alpha = alpha + x * 20% /totalSp + uint256 feeAmount = _amount.mul(feeRatio.div(_BASE_AMOUNT_)); + alpha = alpha.add(feeAmount.div(totalSp)); + + //TODO 3. 这部分税会继续拆成两部分,第一部分销毁,第二部分分给所有vDODO持有人 - uint256 newTotalOverdraft = totalOverdraft.sub(overdraft); - _updateTotalOverdraft(newTotalOverdraft); - - // total sp - (x+x*0.1)//TODO - uint256 newTotalVdodoAmount = totalVdodoAmount.sub(_amount.mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); - _updateTotalVdodoAmount(newTotalVdodoAmount); - - lastRewardBlock = block.number; - - uint256 feeRatio = checkReward(); - // alpha* x * 80% transfer to user - uint256 dodoAmount = alpha.mul(_amount); - - uint256 dodoFee = dodoAmount.mul(feeRatio).div(_BASE_AMOUNT_); - uint256 dodoReceive = dodoAmount.sub(dodoFee); - - dodo.safeTransferFrom(address(this), msg.sender, dodoReceive); - uint256 newRedeem = user.totalRedeem.add(dodoReceive); - _updateUserRedeem(msg.sender,newRedeem); - - // 3. 这部分税会继续拆成两部分,第一部分销毁,第二部分分给所有vDODO持有人 - uint256 distributeAmount = dodoFee; - //是否需要销毁 - if(dodoFeeDestroyRatio>0){ - // uint256 dodoFee = dodoAmount.mul(feeRatio).div(_BASE_AMOUNT_); - uint256 destroyAmount = dodoFee.mul(dodoFeeDestroyRatio).div(_BASE_AMOUNT_); - transfer(_DESTROY_ADDRESS_, destroyAmount); - distributeAmount = dodoFee.sub(destroyAmount); - } - - //可以设置 - - // alpha = alpha*X + x * 20% /totalSp - uint256 feeAmount = _amount.mul(distributeAmount).div(_BASE_AMOUNT_); - - alpha = alpha.mul(_amount).add(feeAmount.div(totalVdodoAmount)); emit Redeem(msg.sender, _amount); } + + //TODO + function checkReward(uint256 _amount) internal returns(uint256) { + + // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 (5≤ y ≤ 15) + + // y = 5 (x ≤ 1) + + // y = 15 (x ≥ 10) + } // balanceOf = sp-DODO欠款/alpha function balanceOf(address _address) public view override returns (uint256 balance) { UserInfo memory user = userInfo[_address]; - balance = user.vdodoAmount.sub(user.overdraft.div(alpha)); + balance = user.spAmount.sub(user.overdraft.div(alpha)); } function transfer(address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(msg.sender, recipient, amount); + _transfer(_msgSender(), recipient, amount); return true; } - function approve(address spender, uint256 amount) public virtual override returns (bool) { - _approve(msg.sender, spender, amount); - return true; - } - function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(sender, recipient, amount); - _approve(sender, msg.sender, _ALLOWED_[sender][msg.sender].sub(amount)); - return true; - } - function _approve( - address owner, - address spender, - uint256 amount - ) private onlyOperater { - _ALLOWED_[owner][spender] = amount; - emit Approval(owner, spender, amount); - } - - function allowance(address owner, address spender) public view override returns (uint256) { - return _ALLOWED_[owner][spender]; - } - - function _transfer(address from, address to, uint256 _amount) internal onlyOperater virtual { - require(from != address(0), " transfer from the zero address"); - require(to != address(0), " transfer to the zero address"); + function _transfer(address sender, address recipient, uint256 _amount) internal onlyOperater virtual override { + require(sender != address(0), " transfer from the zero address"); + require(recipient != address(0), " transfer to the zero address"); // require(balanceOf(from)≥amount) - require(balanceOf(from) >= _amount,"no enough to transfer"); - UserInfo memory user = userInfo[from]; - // sp[from] -= amount - uint256 fromSpAmount = user.vdodoAmount.sub(_amount); - _updateUserData(from,fromSpAmount,0); + require(balanceOf(sender) >= _amount,"no enough to sp transfer"); + UserInfo storage user = userInfo[sender]; + // sp[msg.sender] -= amount + user.spAmount = user.spAmount.sub(_amount); // sp[上级[from]] -= amount*0.1 (round down) - UserInfo memory fromSuperior = userInfo[user.superior]; - uint256 fromSuperiorSpAmount = fromSuperior.vdodoAmount.sub(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); - _updateUserData(user.superior,fromSuperiorSpAmount,0); + UserInfo storage uplineUser = userInfo[user.upline]; + uplineUser.spAmount = uplineUser.spAmount.sub(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); - UserInfo memory toUser = userInfo[to]; + UserInfo storage recipientUser = userInfo[recipient]; // sp[to] += amount - uint256 toSpAmount = toUser.vdodoAmount.add(_amount); - _updateUserData(to,toSpAmount,0); + recipientUser.spAmount = recipientUser.spAmount.add(_amount); - // sp[上级[to]] += amount*0.1 - UserInfo memory toSuperior = userInfo[toUser.superior]; - uint256 toSuperiorSpAmount =toSuperior.vdodoAmount.add(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); - _updateUserData(toUser.superior,toSuperiorSpAmount,0); - - emit Transfer(from, to, _amount); + UserInfo storage recipientUplineUser = userInfo[recipientUser.upline]; + recipientUplineUser.spAmount =recipientUplineUser.spAmount.add(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); + emit Transfer(sender, recipient, _amount); } // 可提取DODO数额 = sp*alpha - DODO欠款 function canWithDraw(address _address) public view returns (uint256 withDrawAmount) { UserInfo memory user = userInfo[_address]; - withDrawAmount = user.vdodoAmount.mul(alpha).sub(user.overdraft); + withDrawAmount = user.spAmount.mul(alpha).sub(user.overdraft); } - function checkUserInfo(address _userAddress) public view returns(address,uint256,uint256,uint256,bool) { + function checkUserInfo(address _userAddress) public view returns(uint256,address,uint256,uint256,uint256,uint256,bool) { UserInfo memory user = userInfo[_userAddress]; - return (user.superior, user.vdodoAmount, user.overdraft,user.totalRedeem,user.hasParticipateGov); + return (user.dodoAmount, user.upline, user.spAmount, user.overdraft,user.lastRewardBlock,user.totalRedeem,user.hasParticipateGov); } - // ============ internal function ============ - function _updateAlpha() internal { - // accuDODO = dodoPerBlock*(block-lastRewardBlock) - uint256 accuDODO = dodoPerBlock * (block.number.sub(lastRewardBlock)); - if(totalVdodoAmount > 0){ - // alpha = alpha + accuDODO/totalSp (round down) - alpha = alpha.add(accuDODO.div(totalVdodoAmount)); - } - } - function _updateUserData(address _who,uint256 _vdodoAmount,uint256 _overdraft) internal { - UserInfo storage userWho = userInfo[_who]; - if(_vdodoAmount>0){ - userWho.vdodoAmount = _vdodoAmount; - } - if(_overdraft>0){ - userWho.overdraft = _overdraft; - } - } - function _updateUserRedeem(address _who,uint256 _newRedeem) internal { - if(_newRedeem>0){ - UserInfo storage userWho = userInfo[_who]; - userWho.totalRedeem = _newRedeem; - } - } - function _updateSuperiorAddress(address _who,address _newAddres) internal { - UserInfo storage userWho = userInfo[_who]; - userWho.superior = _newAddres; - } - function _changeUserParticipateState(address _who,bool _newState) internal { - UserInfo storage userWho = userInfo[_who]; - userWho.hasParticipateGov = _newState; - } - - function _updateTotalOverdraft(uint256 _overdraft) internal { - totalOverdraft = _overdraft; - } - - function _updateTotalVdodoAmount(uint256 _newVdodoAmount) internal { - totalVdodoAmount = _newVdodoAmount; - } - // ============= Helper and calculation function =============== - function checkReward() internal returns(uint256) { - uint256 dodoTotalAmout = IHelper(helperAddr).getDodoAmount(); - // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 ==> y = sqrt(100* (x*x +2x ) / 81)) +15 - // y = 5 (x ≤ 1) - // y = 15 (x ≥ 10) - uint256 x = dodoTotalAmout.divCeil(totalVdodoAmount); - if(x<=_MIN_X_){ - return _MIN_X_Y_; - }else if(x>=_MAX_X_){ - return _MAX_X_Y_; - }else{ - uint256 rewardAmount = x.mul(x).add(x.mul(2)).mul(100).div(81).sqrt().add(15); - return rewardAmount; - } - } } +// deposit 是否需要输入上级地址 +// round up & round down +//vDODO的分红 +// - -//官方的收益会定期回购DODO Token并分红。因此要留一个donate接口,方便外部注入资金----> 为什么不可以使用 DODOToken.transfer? - -// TODO DecimalMath calculation \ No newline at end of file From f78814dd5a46419c54a8f1f366ed273a8f594785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Sun, 31 Jan 2021 16:24:37 +0800 Subject: [PATCH 05/42] update Vdodo --- contracts/VDODO/VDODOChef.sol | 1230 ++++++++------------------------- 1 file changed, 284 insertions(+), 946 deletions(-) diff --git a/contracts/VDODO/VDODOChef.sol b/contracts/VDODO/VDODOChef.sol index fda7398..5a7f4fc 100644 --- a/contracts/VDODO/VDODOChef.sol +++ b/contracts/VDODO/VDODOChef.sol @@ -4,1054 +4,392 @@ SPDX-License-Identifier: Apache-2.0 */ +import {IERC20} from "../intf/IERC20.sol"; +import {Address} from "../lib/Address.sol"; +import {SafeMath} from "../lib/SafeMath.sol"; +import {DecimalMath} from "../lib/DecimalMath.sol"; +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; +import {SafeERC20} from "../lib/SafeERC20.sol"; +import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; pragma solidity 0.6.9; -pragma experimental ABIEncoderV2; - -/** - * @title SafeMath - * @author DODO Breeder - * - * @notice Math operations with safety checks that revert on error - */ -library SafeMath { - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - if (a == 0) { - return 0; - } - - uint256 c = a * b; - require(c / a == b, "MUL_ERROR"); - - return c; - } - - function div(uint256 a, uint256 b) internal pure returns (uint256) { - require(b > 0, "DIVIDING_ERROR"); - return a / b; - } - - function divCeil(uint256 a, uint256 b) internal pure returns (uint256) { - uint256 quotient = div(a, b); - uint256 remainder = a - quotient * b; - if (remainder > 0) { - return quotient + 1; - } else { - return quotient; - } - } - - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - require(b <= a, "SUB_ERROR"); - return a - b; - } - - function add(uint256 a, uint256 b) internal pure returns (uint256) { - uint256 c = a + b; - require(c >= a, "ADD_ERROR"); - return c; - } - - function sqrt(uint256 x) internal pure returns (uint256 y) { - uint256 z = x / 2 + 1; - y = x; - while (z < y) { - y = z; - z = (x / z + z) / 2; - } - } +interface IGovernance { + function governanceCall(address account, uint256 amount,bytes calldata data) external returns (bool); } pragma solidity 0.6.9; -/** - * @title DecimalMath - * @author DODO Breeder - * - * @notice Functions for fixed point number with 18 decimals - */ -library DecimalMath { +interface IHelper { + function getDodoAmount() external returns (uint256); +} +pragma solidity 0.6.9; +contract VDODOToken is IERC20,InitializableOwnable ,ReentrancyGuard{ using SafeMath for uint256; + using SafeERC20 for IERC20; - uint256 internal constant ONE = 10**18; - uint256 internal constant ONE2 = 10**36; - - function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(d) / (10**18); - } - - function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(d).divCeil(10**18); - } - - function divFloor(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(10**18).div(d); - } - - function divCeil(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(10**18).divCeil(d); - } - - function reciprocalFloor(uint256 target) internal pure returns (uint256) { - return uint256(10**36).div(target); - } - - function reciprocalCeil(uint256 target) internal pure returns (uint256) { - return uint256(10**36).divCeil(target); - } -} -// File: @openzeppelin/contracts/access/Ownable.sol - - - -pragma solidity 0.6.9; -/** - * @title Ownable - * @author DODO Breeder - * - * @notice Ownership related functions - */ -contract Ownable { - address public _OWNER_; - address public _NEW_OWNER_; - - // ============ Events ============ - - event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner); - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - // ============ Modifiers ============ - - modifier onlyOwner() { - require(msg.sender == _OWNER_, "NOT_OWNER"); - _; - } - - // ============ Functions ============ - - constructor() internal { - _OWNER_ = msg.sender; - emit OwnershipTransferred(address(0), _OWNER_); - } - - function transferOwnership(address newOwner) external onlyOwner { - emit OwnershipTransferPrepared(_OWNER_, newOwner); - _NEW_OWNER_ = newOwner; - } - - function claimOwnership() external { - require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM"); - emit OwnershipTransferred(_OWNER_, _NEW_OWNER_); - _OWNER_ = _NEW_OWNER_; - _NEW_OWNER_ = address(0); - } -} - - -pragma solidity 0.6.9; - -/** - * @title ReentrancyGuard - * @author DODO Breeder - * - * @notice Protect functions from Reentrancy Attack - */ -contract ReentrancyGuard { - // https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=zero-state#scoping-and-declarations - // zero-state of _ENTERED_ is false - bool private _ENTERED_; - - modifier preventReentrant() { - require(!_ENTERED_, "REENTRANT"); - _ENTERED_ = true; - _; - _ENTERED_ = false; - } -} - -// File: @openzeppelin/contracts/token/ERC20/IERC20.sol - - - -pragma solidity 0.6.9; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address recipient, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `sender` to `recipient` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); - - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); -} - -pragma solidity 0.6.9; - -/* - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with GSN meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address payable) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes memory) { - this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 - return msg.data; - } -} - -pragma solidity 0.6.9; -/** - * @title SafeERC20 - * @dev Wrappers around ERC20 operations that throw on failure (when the token - * contract returns false). Tokens that return no value (and instead revert or - * throw on failure) are also supported, non-reverting calls are assumed to be - * successful. - * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, - * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. - */ -library SafeERC20 { - using SafeMath for uint256; - - function safeTransfer( - IERC20 token, - address to, - uint256 value - ) internal { - _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); - } - - function safeTransferFrom( - IERC20 token, - address from, - address to, - uint256 value - ) internal { - _callOptionalReturn( - token, - abi.encodeWithSelector(token.transferFrom.selector, from, to, value) - ); - } - - function safeApprove( - IERC20 token, - address spender, - uint256 value - ) internal { - // safeApprove should only be called when setting an initial allowance, - // or when resetting it to zero. To increase and decrease it, use - // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' - // solhint-disable-next-line max-line-length - require( - (value == 0) || (token.allowance(address(this), spender) == 0), - "SafeERC20: approve from non-zero to non-zero allowance" - ); - _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); - } - - /** - * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement - * on the return value: the return value is optional (but if data is returned, it must not be false). - * @param token The token targeted by the call. - * @param data The call data (encoded using abi.encode or one of its variants). - */ - function _callOptionalReturn(IERC20 token, bytes memory data) private { - // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since - // we're implementing it ourselves. - - // A Solidity high level call has three parts: - // 1. The target address is checked to verify it contains contract code - // 2. The call itself is made, and success asserted - // 3. The return value is decoded, which in turn checks the size of the returned data. - // solhint-disable-next-line max-line-length - - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory returndata) = address(token).call(data); - require(success, "SafeERC20: low-level call failed"); - - if (returndata.length > 0) { - // Return data is optional - // solhint-disable-next-line max-line-length - require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); - } - } -} - -// File: @openzeppelin/contracts/utils/Address.sol -pragma solidity 0.6.9; - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - */ - function isContract(address account) internal view returns (bool) { - // According to EIP-1052, 0x0 is the value returned for not-yet created accounts - // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned - // for accounts without code, i.e. `keccak256('')` - bytes32 codehash; - bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; - // solhint-disable-next-line no-inline-assembly - assembly { codehash := extcodehash(account) } - return (codehash != accountHash && codehash != 0x0); - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - // solhint-disable-next-line avoid-low-level-calls, avoid-call-value - (bool success, ) = recipient.call{ value: amount }(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain`call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { - return _functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - return _functionCallWithValue(target, data, value, errorMessage); - } - - function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { - require(isContract(target), "Address: call to non-contract"); - - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - - // solhint-disable-next-line no-inline-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} - -pragma solidity 0.6.9; - -/** - * @dev Implementation of the {IERC20} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * For a generic mechanism see {ERC20PresetMinterPauser}. - * - * TIP: For a detailed writeup see our guide - * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. - * - * We have followed general OpenZeppelin guidelines: functions revert instead - * of returning `false` on failure. This behavior is nonetheless conventional - * and does not conflict with the expectations of ERC20 applications. - * - * Additionally, an {Approval} event is emitted on calls to {transferFrom}. - * This allows applications to reconstruct the allowance for all accounts just - * by listening to said events. Other implementations of the EIP may not emit - * these events, as it isn't required by the specification. - * - * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} - * functions have been added to mitigate the well-known issues around setting - * allowances. See {IERC20-approve}. - */ -contract ERC20 is Context, IERC20 { - using SafeMath for uint256; - using Address for address; - - bool cantransfer; - uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; + bool cantransfer; + address govAddr; + address helperAddr; + IERC20 dodo; - mapping (address => uint256) private _balances; - mapping (address => mapping (address => uint256)) private _allowances; + + uint256 public alpha = 100; + uint256 public totalVdodoAmount; + uint256 public totalOverdraft; + + uint256 public dodoPerBlock = 1e18;//TODO + uint256 public lastRewardBlock ; + uint256 public dodoFeeDestroyRatio ; + uint256 constant public _MAG_SP_AMOUNT_ = 10; + uint256 constant public _MAG_TOTALSP_AMOUNT_ = 110; + uint256 constant public _BASE_AMOUNT_ = 100; + address constant public _DESTROY_ADDRESS_ = 0x0000000000000000000000000000000000000000; + + uint256 constant public _MIN_X_ = 1; + uint256 constant public _MIN_X_Y_ = 5; + uint256 constant public _MAX_X_ = 10; + uint256 constant public _MAX_X_Y_ = 15; + + mapping(address => mapping(address => uint256)) internal _ALLOWED_; mapping(address => bool) public operater; + mapping(address => UserInfo) public userInfo; + + struct UserInfo { + address superior; + uint256 vdodoAmount; + uint256 overdraft; + uint256 totalRedeem; + bool hasParticipateGov; //是否正在参与治理,是的话就不可以提币 + } + + + // ============ Events ============ + event ParticipatoryGov(address indexed user, uint256 amount); + event Deposit(address indexed user,address indexed superior, uint256 amount); + event Redeem(address indexed user, uint256 amount); + event SetCantransfer(bool allowed); + event RemoveOperation(address indexed operater); + event AddOperation(address indexed operater); + event ChangePerReward(uint256 dodoPerBlock); + event UpdateDodoFeeDestroyRatio(uint256 dodoFeeDestroyRatio); + event Transfer(address indexed from, address indexed to, uint256 amount); + event Approval(address indexed owner, address indexed spender, uint256 amount); // ============ Modifiers ============ modifier onlyOperater() { require(cantransfer || operater[msg.sender] , "not allowed transfer"); _; } - /** - * @dev Sets the values for {name} and {symbol}, initializes {decimals} with - * a default value of 18. - * - * To select a different value for {decimals}, use {_setupDecimals}. - * - * All three of these values are immutable: they can only be set once during - * construction. - */ - constructor (string memory name, string memory symbol) public { - _name = name; - _symbol = symbol; - _decimals = 18; - } - - /** - * @dev Returns the name of the token. - */ - function name() public view returns (string memory) { - return _name; - } - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() public view returns (string memory) { - return _symbol; - } - - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5,05` (`505 / 10 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is - * called. - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. - */ - function decimals() public view returns (uint8) { - return _decimals; - } - - /** - * @dev See {IERC20-totalSupply}. - */ - function totalSupply() public view override returns (uint256) { - return _totalSupply; - } - - /** - * @dev See {IERC20-balanceOf}. - */ - function balanceOf(address account) public view virtual override returns (uint256) { - return _balances[account]; - } - - /** - * @dev See {IERC20-transfer}. - * - * Requirements: - * - * - `recipient` cannot be the zero address. - * - the caller must have a balance of at least `amount`. - */ - function transfer(address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(_msgSender(), recipient, amount); - return true; - } - - /** - * @dev See {IERC20-allowance}. - */ - function allowance(address owner, address spender) public view virtual override returns (uint256) { - return _allowances[owner][spender]; - } - - /** - * @dev See {IERC20-approve}. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function approve(address spender, uint256 amount) public virtual override returns (bool) { - _approve(_msgSender(), spender, amount); - return true; - } - - /** - * @dev See {IERC20-transferFrom}. - * - * Emits an {Approval} event indicating the updated allowance. This is not - * required by the EIP. See the note at the beginning of {ERC20}; - * - * Requirements: - * - `sender` and `recipient` cannot be the zero address. - * - `sender` must have a balance of at least `amount`. - * - the caller must have allowance for ``sender``'s tokens of at least - * `amount`. - */ - function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(sender, recipient, amount); - _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount)); - return true; - } - - /** - * @dev Atomically increases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { - _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); - return true; - } - - /** - * @dev Atomically decreases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - * - `spender` must have allowance for the caller of at least - * `subtractedValue`. - */ - function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { - _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue)); - return true; - } - - /** - * @dev Moves tokens `amount` from `sender` to `recipient`. - * - * This is internal function is equivalent to {transfer}, and can be used to - * e.g. implement automatic token fees, slashing mechanisms, etc. - * - * Emits a {Transfer} event. - * - * Requirements: - * - * - `sender` cannot be the zero address. - * - `recipient` cannot be the zero address. - * - `sender` must have a balance of at least `amount`. - */ - function _transfer(address sender, address recipient, uint256 amount) internal onlyOperater virtual { - require(sender != address(0), "ERC20: transfer from the zero address"); - require(recipient != address(0), "ERC20: transfer to the zero address"); - - _beforeTokenTransfer(sender, recipient, amount); - - _balances[sender] = _balances[sender].sub(amount); - _balances[recipient] = _balances[recipient].add(amount); - emit Transfer(sender, recipient, amount); - } - - /** @dev Creates `amount` tokens and assigns them to `account`, increasing - * the total supply. - * - * Emits a {Transfer} event with `from` set to the zero address. - * - * Requirements - * - * - `to` cannot be the zero address. - */ - function _mint(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: mint to the zero address"); - - _beforeTokenTransfer(address(0), account, amount); - - _totalSupply = _totalSupply.add(amount); - _balances[account] = _balances[account].add(amount); - emit Transfer(address(0), account, amount); - } - - /** - * @dev Destroys `amount` tokens from `account`, reducing the - * total supply. - * - * Emits a {Transfer} event with `to` set to the zero address. - * - * Requirements - * - * - `account` cannot be the zero address. - * - `account` must have at least `amount` tokens. - */ - function _burn(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: burn from the zero address"); - - _beforeTokenTransfer(account, address(0), amount); - - _balances[account] = _balances[account].sub(amount); - _totalSupply = _totalSupply.sub(amount); - emit Transfer(account, address(0), amount); - } - - /** - * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. - * - * This is internal function is equivalent to `approve`, and can be used to - * e.g. set automatic allowances for certain subsystems, etc. - * - * Emits an {Approval} event. - * - * Requirements: - * - * - `owner` cannot be the zero address. - * - `spender` cannot be the zero address. - */ - function _approve(address owner, address spender, uint256 amount) internal onlyOperater virtual { - require(owner != address(0), "ERC20: approve from the zero address"); - require(spender != address(0), "ERC20: approve to the zero address"); - - _allowances[owner][spender] = amount; - emit Approval(owner, spender, amount); - } - - /** - * @dev Sets {decimals} to a value other than the default one of 18. - * - * WARNING: This function should only be called from the constructor. Most - * applications that interact with token contracts will not expect - * {decimals} to ever change, and may work incorrectly if it does. - */ - function _setupDecimals(uint8 decimals_) internal { - _decimals = decimals_; - } - - function _setCantransfer(bool _cantransfer) internal { - cantransfer = _cantransfer; - } - - function _addOperation(address _operater) internal { - operater[_operater] = true; - } - function _removeOperation(address _operater) internal { - operater[_operater] = false; - } - - /** - * @dev Hook that is called before any transfer of tokens. This includes - * minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * will be to transferred to `to`. - * - when `from` is zero, `amount` tokens will be minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens will be burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } -} -pragma solidity 0.6.9; -interface IGovernance { - function governanceCall(address account, uint256 amount,bytes calldata data) external returns (bool); - -} -pragma solidity 0.6.9; -contract SpToken is ERC20("StakingPowerToken", "SPT"), Ownable ,ReentrancyGuard{ - using SafeMath for uint256; - using SafeERC20 for IERC20; - - address govAddr; - IERC20 dodo; - - uint256 public alpha = 1; - uint256 public totalSp; - uint256 public totalOverdraft; - - uint256 public _DODOPERBLOCK_ = 1e18;//TODO - uint256 constant public _MAG_SP_AMOUNT_ = 10; - uint256 constant public _MAG_TOTALSP_AMOUNT_ = 110; - uint256 constant public _BASE_AMOUNT_ = 100; - - struct UserInfo { - uint256 dodoAmount; - address upline; - uint256 spAmount; - uint256 overdraft; - uint256 lastRewardBlock; - uint256 totalRedeem; - bool hasParticipateGov; //是否正在参与治理,是的话就不可以提币 - } - - mapping (address => UserInfo) public userInfo; - -// ============ Events ============ - event ParticipatoryGov(address indexed user, uint256 amount); - event Deposit(address indexed user,address indexed upline, uint256 amount); - event Redeem(address indexed user, uint256 amount); - event SetCantransfer(bool allowed); - event RemoveOperation(address indexed _operater); - event AddOperation(address indexed _operater); - receive() external payable { revert(); } - // ============ Functions ============ + constructor( address _govAddr, - address _dodo - - ) public { + address _dodo, + address _helperAddr, + string memory name, + string memory symbol) + public { + _name = name; + _symbol = symbol; + _decimals = 18; govAddr = _govAddr; + helperAddr = _helperAddr; + dodo = IERC20(_dodo); } - - - function mint(address _to, uint256 _amount) public onlyOwner { - _mint(_to, _amount); + function name() public view override returns (string memory) { + return _name; } - function burn(address _to, uint256 amount) public onlyOwner{ - _burn(_to,amount); + function symbol() public view override returns (string memory) { + return _symbol; } + function decimals() public view override returns (uint8) { + return _decimals; + } + function totalSupply() public view override returns (uint256) { + return totalVdodoAmount; + } + + // ============ Ownable Functions ============ + function setCantransfer(bool _allowed) public onlyOwner { - _setCantransfer(_allowed); + cantransfer = _allowed; emit SetCantransfer(_allowed); } - function addOperationAddress(address _operationAddress) public onlyOwner { - _addOperation(_operationAddress); - emit AddOperation(_operationAddress); + function addOperationAddress(address _operater) public onlyOwner { + operater[_operater] = true; + emit AddOperation(_operater); } - function removeOperation(address _operationAddress) public onlyOwner { - _removeOperation(_operationAddress); - emit RemoveOperation(_operationAddress); + function removeOperation(address _operater) public onlyOwner { + operater[_operater] = false; + emit RemoveOperation(_operater); } + function changePerReward(uint256 _dodoPerBlock) public onlyOwner { + dodoPerBlock = _dodoPerBlock; + emit ChangePerReward(dodoPerBlock); + } + function updateDodoFeeDestroyRatio(uint256 _dodoFeeDestroyRatio) public onlyOwner { + dodoFeeDestroyRatio = _dodoFeeDestroyRatio; + emit UpdateDodoFeeDestroyRatio(_dodoFeeDestroyRatio); + } + + + // ============ Functions ============ + + //TODO 投票与代理是否分开 function participatoryGov( uint256 _amount, bytes calldata _data ) external preventReentrant { - UserInfo storage user = userInfo[msg.sender]; - require(user.spAmount>_amount,"no enough sp"); + UserInfo memory user = userInfo[msg.sender]; + require(user.vdodoAmount>_amount,"no enough vdodo"); if (_data.length > 0) IGovernance(govAddr).governanceCall(msg.sender, _amount, _data); - user.spAmount = user.spAmount.sub(_amount); - user.hasParticipateGov = true; + + uint256 userVdodoAmount = user.vdodoAmount.sub(_amount); + _updateUserData(msg.sender,userVdodoAmount,0); + totalVdodoAmount = totalVdodoAmount.sub(_amount); + _changeUserParticipateState(msg.sender,true); emit ParticipatoryGov(msg.sender, _amount); } - //TODO _uplineAddress??? deposit again???? //TODO round up /down - function deposit(uint256 _amount,address _uplineAddress) public preventReentrant{ + function deposit(uint256 _amount,address _superiorAddress) public preventReentrant{ require(_amount>0,"must deposit greater than 0"); - dodo.transferFrom(msg.sender, address(this), _amount); + _updateAlpha(); - UserInfo storage user = userInfo[msg.sender]; - if(user.dodoAmount==0){ - user.lastRewardBlock = block.number; - } - user.dodoAmount = user.dodoAmount.add(_amount); - // accuDODO = _DODOPERBLOCK_*(block-lastRewardBlock) - uint256 accuDODO = _DODOPERBLOCK_ * (block.number.sub(user.lastRewardBlock)); - //TODO FIRST DEPOSIT??? - if(totalSp > 0){ - // alpha = alpha + accuDODO/totalSp (round down) - alpha = alpha.add(accuDODO.div(totalSp)); - } - + UserInfo memory user = userInfo[msg.sender]; // 自己的sp + x/alpha - uint256 newSpAmount = _amount.div(alpha); + uint256 newVdodoAmount = _amount.div(alpha); + uint256 fromVdodoAmount = user.vdodoAmount.add(newVdodoAmount); + _updateUserData(msg.sender,fromVdodoAmount,0); - _mint(msg.sender,newSpAmount); - // spToken.mint(msg.sender,newSpAmount); - - user.spAmount = user.spAmount.add(newSpAmount); - if(user.upline==address(0x0)){ - user.upline = _uplineAddress; + if(user.superior==address(0x0) && _superiorAddress != address(0x0)){ + _updateSuperiorAddress(msg.sender,_superiorAddress); } - UserInfo storage uplineUser = userInfo[user.upline]; + + UserInfo memory superiorUser = userInfo[user.superior]; // 上级sp +( x/alpha)* 0.1 (round up) - uplineUser.spAmount = uplineUser.spAmount.add( - _amount.mul(alpha) + uint256 superiorVdodoAmount = superiorUser.vdodoAmount.add( + _amount.div(alpha) .mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); // 上级DODO欠款 + x*0.1 (round up) uint256 overdraft = _amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_); - uplineUser.overdraft = uplineUser.overdraft.add(overdraft); + uint256 superiorOverdraft = superiorUser.overdraft.add(overdraft); - totalOverdraft = totalOverdraft.add(overdraft); + _updateUserData(user.superior,superiorVdodoAmount,superiorOverdraft); + + uint256 newTotalOverdraft = totalOverdraft.add(overdraft); + _updateTotalOverdraft(newTotalOverdraft); // total sp + x/alpha*1.1 - totalSp = totalSp.add(_amount.div(alpha).mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); - emit Deposit(msg.sender,_uplineAddress, _amount); + uint256 newTotalVdodoAmount = totalVdodoAmount.add(_amount.div(alpha).mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); + _updateTotalVdodoAmount(newTotalVdodoAmount); + emit Deposit(msg.sender,_superiorAddress, _amount); } + //TODO round up /down - function redeem(uint256 _amount) public preventReentrant{ - UserInfo storage user = userInfo[msg.sender]; - require(user.spAmount>_amount,"no enough sp token"); + UserInfo memory user = userInfo[msg.sender]; + require(user.vdodoAmount>=_amount,"no enough vdodo token"); require(!user.hasParticipateGov,"hasParticipateGov"); + _updateAlpha(); - - // accuDODO = _DODOPERBLOCK_*(block-lastRewardBlock) - uint256 accuDODO = _DODOPERBLOCK_ * (block.number.sub(user.lastRewardBlock)); - // alpha = alpha + accuDODO/totalSp (round down) - alpha = alpha.add(accuDODO.div(totalSp)); // 自己的sp - x - _burn(msg.sender,_amount); - // spToken.burn(msg.sender,_amount); - user.spAmount = user.spAmount.sub(_amount); + uint256 userVdodoAmount = user.vdodoAmount.sub(_amount); + _updateUserData(msg.sender,userVdodoAmount,0); - UserInfo storage uplineUser = userInfo[user.upline]; + UserInfo memory superiorUser = userInfo[user.superior]; // 上级sp - (x)*0.1(round down) - uplineUser.spAmount = uplineUser.spAmount.sub( + uint256 superiorVdodoAmount = superiorUser.vdodoAmount.sub( _amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); // 上级DODO欠款 - x*alpha*0.1 (round down) uint256 overdraft = _amount.mul(alpha).mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_); - uplineUser.overdraft = uplineUser.overdraft.sub(overdraft); - - totalOverdraft = totalOverdraft.sub(overdraft); - - // total sp - x*1.1 - totalSp = totalSp.sub(_amount.mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); - - user.lastRewardBlock = block.number; - - uint256 feeRatio = checkReward(_amount); - - // x * 80% transfer to user - uint256 receiveAmount = _amount.mul(_BASE_AMOUNT_.sub(feeRatio).div(_BASE_AMOUNT_)); - - dodo.safeTransferFrom(address(this), msg.sender, receiveAmount); - user.dodoAmount = user.dodoAmount.sub(receiveAmount); - user.totalRedeem = user.totalRedeem.add(receiveAmount); - - // alpha = alpha + x * 20% /totalSp - uint256 feeAmount = _amount.mul(feeRatio.div(_BASE_AMOUNT_)); - alpha = alpha.add(feeAmount.div(totalSp)); - - //TODO 3. 这部分税会继续拆成两部分,第一部分销毁,第二部分分给所有vDODO持有人 + uint256 superiorOverdraft= superiorUser.overdraft.sub(overdraft); + _updateUserData(user.superior,superiorVdodoAmount,superiorOverdraft); + uint256 newTotalOverdraft = totalOverdraft.sub(overdraft); + _updateTotalOverdraft(newTotalOverdraft); + + // total sp - (x+x*0.1)//TODO + uint256 newTotalVdodoAmount = totalVdodoAmount.sub(_amount.mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); + _updateTotalVdodoAmount(newTotalVdodoAmount); + + lastRewardBlock = block.number; + + uint256 feeRatio = checkReward(); + // alpha* x * 80% transfer to user + uint256 dodoAmount = alpha.mul(_amount); + + uint256 dodoFee = dodoAmount.mul(feeRatio).div(_BASE_AMOUNT_); + uint256 dodoReceive = dodoAmount.sub(dodoFee); + + dodo.safeTransferFrom(address(this), msg.sender, dodoReceive); + uint256 newRedeem = user.totalRedeem.add(dodoReceive); + _updateUserRedeem(msg.sender,newRedeem); + + // 3. 这部分税会继续拆成两部分,第一部分销毁,第二部分分给所有vDODO持有人 + uint256 distributeAmount = dodoFee; + //是否需要销毁 + if(dodoFeeDestroyRatio>0){ + // uint256 dodoFee = dodoAmount.mul(feeRatio).div(_BASE_AMOUNT_); + uint256 destroyAmount = dodoFee.mul(dodoFeeDestroyRatio).div(_BASE_AMOUNT_); + transfer(_DESTROY_ADDRESS_, destroyAmount); + distributeAmount = dodoFee.sub(destroyAmount); + } + + //可以设置 + + // alpha = alpha*X + x * 20% /totalSp + uint256 feeAmount = _amount.mul(distributeAmount).div(_BASE_AMOUNT_); + + alpha = alpha.mul(_amount).add(feeAmount.div(totalVdodoAmount)); emit Redeem(msg.sender, _amount); } - - //TODO - function checkReward(uint256 _amount) internal returns(uint256) { - - // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 (5≤ y ≤ 15) - - // y = 5 (x ≤ 1) - - // y = 15 (x ≥ 10) - } // balanceOf = sp-DODO欠款/alpha function balanceOf(address _address) public view override returns (uint256 balance) { UserInfo memory user = userInfo[_address]; - balance = user.spAmount.sub(user.overdraft.div(alpha)); + balance = user.vdodoAmount.sub(user.overdraft.div(alpha)); } function transfer(address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(_msgSender(), recipient, amount); + _transfer(msg.sender, recipient, amount); return true; } - function _transfer(address sender, address recipient, uint256 _amount) internal onlyOperater virtual override { - require(sender != address(0), " transfer from the zero address"); - require(recipient != address(0), " transfer to the zero address"); + function approve(address spender, uint256 amount) public virtual override returns (bool) { + _approve(msg.sender, spender, amount); + return true; + } + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, msg.sender, _ALLOWED_[sender][msg.sender].sub(amount)); + return true; + } + function _approve( + address owner, + address spender, + uint256 amount + ) private onlyOperater { + _ALLOWED_[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + function allowance(address owner, address spender) public view override returns (uint256) { + return _ALLOWED_[owner][spender]; + } + + function _transfer(address from, address to, uint256 _amount) internal onlyOperater virtual { + require(from != address(0), " transfer from the zero address"); + require(to != address(0), " transfer to the zero address"); // require(balanceOf(from)≥amount) - require(balanceOf(sender) >= _amount,"no enough to sp transfer"); - UserInfo storage user = userInfo[sender]; - // sp[msg.sender] -= amount - user.spAmount = user.spAmount.sub(_amount); + require(balanceOf(from) >= _amount,"no enough to transfer"); + UserInfo memory user = userInfo[from]; + // sp[from] -= amount + uint256 fromSpAmount = user.vdodoAmount.sub(_amount); + _updateUserData(from,fromSpAmount,0); // sp[上级[from]] -= amount*0.1 (round down) - UserInfo storage uplineUser = userInfo[user.upline]; - uplineUser.spAmount = uplineUser.spAmount.sub(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); + UserInfo memory fromSuperior = userInfo[user.superior]; + uint256 fromSuperiorSpAmount = fromSuperior.vdodoAmount.sub(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); + _updateUserData(user.superior,fromSuperiorSpAmount,0); - UserInfo storage recipientUser = userInfo[recipient]; + UserInfo memory toUser = userInfo[to]; // sp[to] += amount - recipientUser.spAmount = recipientUser.spAmount.add(_amount); + uint256 toSpAmount = toUser.vdodoAmount.add(_amount); + _updateUserData(to,toSpAmount,0); - UserInfo storage recipientUplineUser = userInfo[recipientUser.upline]; - recipientUplineUser.spAmount =recipientUplineUser.spAmount.add(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); - emit Transfer(sender, recipient, _amount); + // sp[上级[to]] += amount*0.1 + UserInfo memory toSuperior = userInfo[toUser.superior]; + uint256 toSuperiorSpAmount =toSuperior.vdodoAmount.add(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); + _updateUserData(toUser.superior,toSuperiorSpAmount,0); + + emit Transfer(from, to, _amount); } // 可提取DODO数额 = sp*alpha - DODO欠款 function canWithDraw(address _address) public view returns (uint256 withDrawAmount) { UserInfo memory user = userInfo[_address]; - withDrawAmount = user.spAmount.mul(alpha).sub(user.overdraft); + withDrawAmount = user.vdodoAmount.mul(alpha).sub(user.overdraft); } - function checkUserInfo(address _userAddress) public view returns(uint256,address,uint256,uint256,uint256,uint256,bool) { + function checkUserInfo(address _userAddress) public view returns(address,uint256,uint256,uint256,bool) { UserInfo memory user = userInfo[_userAddress]; - return (user.dodoAmount, user.upline, user.spAmount, user.overdraft,user.lastRewardBlock,user.totalRedeem,user.hasParticipateGov); + return (user.superior, user.vdodoAmount, user.overdraft,user.totalRedeem,user.hasParticipateGov); } + // ============ internal function ============ + function _updateAlpha() internal { + // accuDODO = dodoPerBlock*(block-lastRewardBlock) + uint256 accuDODO = dodoPerBlock * (block.number.sub(lastRewardBlock)); + if(totalVdodoAmount > 0){ + // alpha = alpha + accuDODO/totalSp (round down) + alpha = alpha.add(accuDODO.div(totalVdodoAmount)); + } + } + function _updateUserData(address _who,uint256 _vdodoAmount,uint256 _overdraft) internal { + UserInfo storage userWho = userInfo[_who]; + if(_vdodoAmount>0){ + userWho.vdodoAmount = _vdodoAmount; + } + if(_overdraft>0){ + userWho.overdraft = _overdraft; + } + } + function _updateUserRedeem(address _who,uint256 _newRedeem) internal { + if(_newRedeem>0){ + UserInfo storage userWho = userInfo[_who]; + userWho.totalRedeem = _newRedeem; + } + } + function _updateSuperiorAddress(address _who,address _newAddres) internal { + UserInfo storage userWho = userInfo[_who]; + userWho.superior = _newAddres; + } + function _changeUserParticipateState(address _who,bool _newState) internal { + UserInfo storage userWho = userInfo[_who]; + userWho.hasParticipateGov = _newState; + } + + function _updateTotalOverdraft(uint256 _overdraft) internal { + totalOverdraft = _overdraft; + } + + function _updateTotalVdodoAmount(uint256 _newVdodoAmount) internal { + totalVdodoAmount = _newVdodoAmount; + } + // ============= Helper and calculation function =============== + function checkReward() internal returns(uint256) { + uint256 dodoTotalAmout = IHelper(helperAddr).getDodoAmount(); + // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 ==> y = sqrt(100* (x*x +2x ) / 81)) +15 + // y = 5 (x ≤ 1) + // y = 15 (x ≥ 10) + uint256 x = dodoTotalAmout.divCeil(totalVdodoAmount); + if(x<=_MIN_X_){ + return _MIN_X_Y_; + }else if(x>=_MAX_X_){ + return _MAX_X_Y_; + }else{ + uint256 rewardAmount = x.mul(x).add(x.mul(2)).mul(100).div(81).sqrt().add(15); + return rewardAmount; + } + } } -// deposit 是否需要输入上级地址 -// round up & round down -//vDODO的分红 -// + +//官方的收益会定期回购DODO Token并分红。因此要留一个donate接口,方便外部注入资金----> 为什么不可以使用 DODOToken.transfer? + +// TODO DecimalMath calculation \ No newline at end of file From b680b6fd02091a7065e8e6f8b974899ad611a747 Mon Sep 17 00:00:00 2001 From: radar-bear Date: Sat, 23 Jan 2021 18:35:45 +0800 Subject: [PATCH 06/42] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 844ffcb..4d2835b 100644 --- a/README.md +++ b/README.md @@ -27,4 +27,4 @@ Anyone who reports a unique, previously-unreported vulnerability that results in ## Contact Us -Send E-mail to contact@dodoex.io \ No newline at end of file +Send E-mail to contact@dodoex.io From bfa3fc346695a74eacda2a2ca559940ff9215a90 Mon Sep 17 00:00:00 2001 From: radar-bear Date: Sat, 23 Jan 2021 18:39:36 +0800 Subject: [PATCH 07/42] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 4d2835b..99ceea7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # DODO V2: Help 1 Trillion People Issue Token +## Audit Report + +[Audited by Peckshield](https://github.com/DODOEX/contractV2/blob/main/audit/PeckShield-Audit-DODOV2-v1.0.pdf) + ## Bug Bounty 💰 ### Rewards From 38f5fe142bd934dd8ac7510eddf2307369d24a9a Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 26 Jan 2021 17:53:49 +0800 Subject: [PATCH 08/42] fix apdater && add approve proxy --- contracts/DODOPrivatePool/impl/DPPAdmin.sol | 10 +- contracts/DODOPrivatePool/impl/DPPVault.sol | 1 - contracts/Factory/DPPFactory.sol | 12 +- contracts/SmartRoute/DODOApproveProxy.sol | 91 ++ contracts/SmartRoute/DODOV1Proxy04.sol | 283 ++++++ contracts/SmartRoute/DODOV2Proxy02.sol | 805 ++++++++++++++++++ .../SmartRoute/adapter/DODOV1Adapter.sol | 9 +- deploy-detail-v1.5.txt | 12 + deploy-detail-v2.0.txt | 79 +- kovan-mock-v2.0.txt | 12 + migrations/2_deploy_v1.5.js | 28 +- migrations/3_deploy_v2.js | 122 ++- migrations/4_deploy_v2_mock.js | 38 +- test/utils/Contracts.ts | 3 +- test/utils/ProxyContextV2.ts | 15 +- truffle-config.js | 5 +- 16 files changed, 1417 insertions(+), 108 deletions(-) create mode 100644 contracts/SmartRoute/DODOApproveProxy.sol create mode 100644 contracts/SmartRoute/DODOV1Proxy04.sol create mode 100644 contracts/SmartRoute/DODOV2Proxy02.sol diff --git a/contracts/DODOPrivatePool/impl/DPPAdmin.sol b/contracts/DODOPrivatePool/impl/DPPAdmin.sol index 24d26cb..037b3e4 100644 --- a/contracts/DODOPrivatePool/impl/DPPAdmin.sol +++ b/contracts/DODOPrivatePool/impl/DPPAdmin.sol @@ -9,7 +9,7 @@ pragma solidity 0.6.9; pragma experimental ABIEncoderV2; import {IDPP} from "../intf/IDPP.sol"; -import {IDODOApprove} from "../../intf/IDODOApprove.sol"; +import {IDODOApproveProxy} from "../../SmartRoute/DODOApproveProxy.sol"; import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; /** @@ -21,7 +21,7 @@ import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; contract DPPAdmin is InitializableOwnable { address public _DPP_; address public _OPERATOR_; - address public _DODO_APPROVE_; + address public _DODO_APPROVE_PROXY_; uint256 public _FREEZE_TIMESTAMP_; @@ -34,12 +34,12 @@ contract DPPAdmin is InitializableOwnable { address owner, address dpp, address operator, - address dodoApprove + address dodoApproveProxy ) external { initOwner(owner); _DPP_ = dpp; _OPERATOR_ = operator; - _DODO_APPROVE_ = dodoApprove; + _DODO_APPROVE_PROXY_ = dodoApproveProxy; } function sync() external notFreezed onlyOwner { @@ -74,7 +74,7 @@ contract DPPAdmin is InitializableOwnable { ) external notFreezed returns (bool) { require( msg.sender == _OWNER_ || - (msg.sender == IDODOApprove(_DODO_APPROVE_).getDODOProxy() && + (IDODOApproveProxy(_DODO_APPROVE_PROXY_).isAllowedProxy(msg.sender) && operator == _OPERATOR_), "RESET FORBIDDEN!" ); diff --git a/contracts/DODOPrivatePool/impl/DPPVault.sol b/contracts/DODOPrivatePool/impl/DPPVault.sol index c1e808e..46126eb 100644 --- a/contracts/DODOPrivatePool/impl/DPPVault.sol +++ b/contracts/DODOPrivatePool/impl/DPPVault.sol @@ -14,7 +14,6 @@ import {IDODOCallee} from "../../intf/IDODOCallee.sol"; import {SafeMath} from "../../lib/SafeMath.sol"; import {DecimalMath} from "../../lib/DecimalMath.sol"; import {SafeERC20} from "../../lib/SafeERC20.sol"; -import {Ownable} from "../../lib/Ownable.sol"; import {PMMPricing} from "../../lib/PMMPricing.sol"; contract DPPVault is DPPStorage { diff --git a/contracts/Factory/DPPFactory.sol b/contracts/Factory/DPPFactory.sol index 7c4d52d..727f4e7 100644 --- a/contracts/Factory/DPPFactory.sol +++ b/contracts/Factory/DPPFactory.sol @@ -26,7 +26,7 @@ contract DPPFactory is InitializableOwnable { address public immutable _CLONE_FACTORY_; address public immutable _DEFAULT_MAINTAINER_; address public immutable _DEFAULT_MT_FEE_RATE_MODEL_; - address public immutable _DODO_APPROVE_; + address public immutable _DODO_APPROVE_PROXY_; address public _DPP_TEMPLATE_; address public _DPP_ADMIN_TEMPLATE_; @@ -55,14 +55,14 @@ contract DPPFactory is InitializableOwnable { address dppAdminTemplate, address defaultMaintainer, address defaultMtFeeRateModel, - address dodoApprove + address dodoApproveProxy ) public { _CLONE_FACTORY_ = cloneFactory; _DPP_TEMPLATE_ = dppTemplate; _DPP_ADMIN_TEMPLATE_ = dppAdminTemplate; _DEFAULT_MAINTAINER_ = defaultMaintainer; _DEFAULT_MT_FEE_RATE_MODEL_ = defaultMtFeeRateModel; - _DODO_APPROVE_ = dodoApprove; + _DODO_APPROVE_PROXY_ = dodoApproveProxy; } // ============ Functions ============ @@ -87,7 +87,7 @@ contract DPPFactory is InitializableOwnable { creator, _dppAddress, creator, - _DODO_APPROVE_ + _DODO_APPROVE_PROXY_ ); IDPP(_dppAddress).init( adminModel, @@ -111,10 +111,10 @@ contract DPPFactory is InitializableOwnable { address owner, address dpp, address operator, - address dodoApprove + address dodoApproveProxy ) internal returns (address adminModel) { adminModel = ICloneFactory(_CLONE_FACTORY_).clone(_DPP_ADMIN_TEMPLATE_); - IDPPAdmin(adminModel).init(owner, dpp, operator, dodoApprove); + IDPPAdmin(adminModel).init(owner, dpp, operator, dodoApproveProxy); } // ============ Admin Operation Functions ============ diff --git a/contracts/SmartRoute/DODOApproveProxy.sol b/contracts/SmartRoute/DODOApproveProxy.sol new file mode 100644 index 0000000..de1ae79 --- /dev/null +++ b/contracts/SmartRoute/DODOApproveProxy.sol @@ -0,0 +1,91 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {IDODOApprove} from "../intf/IDODOApprove.sol"; +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; + +interface IDODOApproveProxy { + function isAllowedProxy(address _proxy) external view returns (bool); + function claimTokens(address token,address who,address dest,uint256 amount) external; +} + +/** + * @title DODOApproveProxy + * @author DODO Breeder + * + * @notice Allow different version dodoproxy to claim from DODOApprove + */ +contract DODOApproveProxy is InitializableOwnable { + + // ============ Storage ============ + uint256 private constant _TIMELOCK_DURATION_ = 3 days; + mapping (address => bool) public _IS_ALLOWED_PROXY_; + uint256 public _TIMELOCK_; + address public _PENDING_ADD_DODO_PROXY_; + address public immutable _DODO_APPROVE_; + + // ============ Modifiers ============ + modifier notLocked() { + require( + _TIMELOCK_ <= block.timestamp, + "SetProxy is timelocked" + ); + _; + } + + constructor(address dodoApporve) public { + _DODO_APPROVE_ = dodoApporve; + } + + function init(address owner, address[] memory proxies) external { + initOwner(owner); + for(uint i = 0; i < proxies.length; i++) + _IS_ALLOWED_PROXY_[proxies[i]] = true; + } + + function unlockAddProxy(address newDodoProxy) public onlyOwner { + _TIMELOCK_ = block.timestamp + _TIMELOCK_DURATION_; + _PENDING_ADD_DODO_PROXY_ = newDodoProxy; + } + + function lockAddProxy() public onlyOwner { + _PENDING_ADD_DODO_PROXY_ = address(0); + _TIMELOCK_ = 0; + } + + + function addDODOProxy() external onlyOwner notLocked() { + _IS_ALLOWED_PROXY_[_PENDING_ADD_DODO_PROXY_] = true; + lockAddProxy(); + } + + function removeDODOProxy (address oldDodoProxy) public onlyOwner { + _IS_ALLOWED_PROXY_[oldDodoProxy] = false; + } + + function claimTokens( + address token, + address who, + address dest, + uint256 amount + ) external { + require(_IS_ALLOWED_PROXY_[msg.sender], "DODOApproveProxy:Access restricted"); + IDODOApprove(_DODO_APPROVE_).claimTokens( + token, + who, + dest, + amount + ); + } + + function isAllowedProxy(address _proxy) external view returns (bool) { + return _IS_ALLOWED_PROXY_[_proxy]; + } +} diff --git a/contracts/SmartRoute/DODOV1Proxy04.sol b/contracts/SmartRoute/DODOV1Proxy04.sol new file mode 100644 index 0000000..697091b --- /dev/null +++ b/contracts/SmartRoute/DODOV1Proxy04.sol @@ -0,0 +1,283 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {IERC20} from "../intf/IERC20.sol"; +import {UniversalERC20} from "./lib/UniversalERC20.sol"; +import {SafeMath} from "../lib/SafeMath.sol"; +import {IDODOV1} from "./intf/IDODOV1.sol"; +import {IDODOSellHelper} from "./helper/DODOSellHelper.sol"; +import {IWETH} from "../intf/IWETH.sol"; +import {IChi} from "./intf/IChi.sol"; +import {IUni} from "./intf/IUni.sol"; +import {IDODOApproveProxy} from "./DODOApproveProxy.sol"; +import {IDODOV1Proxy02} from "./intf/IDODOV1Proxy02.sol"; +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; + +/** + * @title DODOV1Proxy04 + * @author DODO Breeder + * + * @notice Entrance of trading in DODO platform + */ +contract DODOV1Proxy04 is IDODOV1Proxy02, InitializableOwnable { + using SafeMath for uint256; + using UniversalERC20 for IERC20; + + // ============ Storage ============ + + address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public immutable _DODO_APPROVE_PROXY_; + address public immutable _DODO_SELL_HELPER_; + address public immutable _WETH_; + address public immutable _CHI_TOKEN_; + uint256 public _GAS_DODO_MAX_RETURN_ = 10; + uint256 public _GAS_EXTERNAL_RETURN_ = 5; + mapping (address => bool) public isWhiteListed; + + // ============ Events ============ + + event OrderHistory( + address indexed fromToken, + address indexed toToken, + address indexed sender, + uint256 fromAmount, + uint256 returnAmount + ); + + // ============ Modifiers ============ + + modifier judgeExpired(uint256 deadLine) { + require(deadLine >= block.timestamp, "DODOV1Proxy04: EXPIRED"); + _; + } + + constructor( + address dodoApproveProxy, + address dodoSellHelper, + address weth, + address chiToken + ) public { + _DODO_APPROVE_PROXY_ = dodoApproveProxy; + _DODO_SELL_HELPER_ = dodoSellHelper; + _WETH_ = weth; + _CHI_TOKEN_ = chiToken; + } + + fallback() external payable {} + + receive() external payable {} + + function updateGasReturn(uint256 newDodoGasReturn, uint256 newExternalGasReturn) public onlyOwner { + _GAS_DODO_MAX_RETURN_ = newDodoGasReturn; + _GAS_EXTERNAL_RETURN_ = newExternalGasReturn; + } + + function addWhiteList (address contractAddr) public onlyOwner { + isWhiteListed[contractAddr] = true; + } + + function removeWhiteList (address contractAddr) public onlyOwner { + isWhiteListed[contractAddr] = false; + } + + function dodoSwapV1( + address fromToken, + address toToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory dodoPairs, + uint256 directions, + uint256 deadLine + ) external override payable judgeExpired(deadLine) returns (uint256 returnAmount) { + require(dodoPairs.length > 0, "DODOV1Proxy04: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV1Proxy04: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV1Proxy04: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV1Proxy04: NOT_SUPPORT_BUY_CHI"); + + uint256 originGas = gasleft(); + + if (fromToken != _ETH_ADDRESS_) { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( + fromToken, + msg.sender, + address(this), + fromTokenAmount + ); + } else { + require(msg.value == fromTokenAmount, "DODOV1Proxy04: ETH_AMOUNT_NOT_MATCH"); + IWETH(_WETH_).deposit{value: fromTokenAmount}(); + } + + for (uint256 i = 0; i < dodoPairs.length; i++) { + address curDodoPair = dodoPairs[i]; + if (directions & 1 == 0) { + address curDodoBase = IDODOV1(curDodoPair)._BASE_TOKEN_(); + require(curDodoBase != _CHI_TOKEN_, "DODOV1Proxy04: NOT_SUPPORT_CHI"); + uint256 curAmountIn = IERC20(curDodoBase).balanceOf(address(this)); + IERC20(curDodoBase).universalApproveMax(curDodoPair, curAmountIn); + IDODOV1(curDodoPair).sellBaseToken(curAmountIn, 0, ""); + } else { + address curDodoQuote = IDODOV1(curDodoPair)._QUOTE_TOKEN_(); + require(curDodoQuote != _CHI_TOKEN_, "DODOV1Proxy04: NOT_SUPPORT_CHI"); + uint256 curAmountIn = IERC20(curDodoQuote).balanceOf(address(this)); + IERC20(curDodoQuote).universalApproveMax(curDodoPair, curAmountIn); + uint256 canBuyBaseAmount = IDODOSellHelper(_DODO_SELL_HELPER_).querySellQuoteToken( + curDodoPair, + curAmountIn + ); + IDODOV1(curDodoPair).buyBaseToken(canBuyBaseAmount, curAmountIn, ""); + } + directions = directions >> 1; + } + + if (toToken == _ETH_ADDRESS_) { + returnAmount = IWETH(_WETH_).balanceOf(address(this)); + IWETH(_WETH_).withdraw(returnAmount); + } else { + returnAmount = IERC20(toToken).tokenBalanceOf(address(this)); + } + + require(returnAmount >= minReturnAmount, "DODOV1Proxy04: Return amount is not enough"); + IERC20(toToken).universalTransfer(msg.sender, returnAmount); + + emit OrderHistory(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount); + + uint256 _gasDodoMaxReturn = _GAS_DODO_MAX_RETURN_; + if(_gasDodoMaxReturn > 0) { + uint256 calcGasTokenBurn = originGas.sub(gasleft()) / 65000; + uint256 gasTokenBurn = calcGasTokenBurn > _gasDodoMaxReturn ? _gasDodoMaxReturn : calcGasTokenBurn; + if(gasleft() > 27710 + gasTokenBurn * 6080) + IChi(_CHI_TOKEN_).freeUpTo(gasTokenBurn); + } + } + + function externalSwap( + address fromToken, + address toToken, + address approveTarget, + address swapTarget, + uint256 fromTokenAmount, + uint256 minReturnAmount, + bytes memory callDataConcat, + uint256 deadLine + ) external override payable judgeExpired(deadLine) returns (uint256 returnAmount) { + require(minReturnAmount > 0, "DODOV1Proxy04: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV1Proxy04: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV1Proxy04: NOT_SUPPORT_BUY_CHI"); + + address _fromToken = fromToken; + address _toToken = toToken; + + uint256 toTokenOriginBalance = IERC20(_toToken).universalBalanceOf(msg.sender); + + if (_fromToken != _ETH_ADDRESS_) { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( + _fromToken, + msg.sender, + address(this), + fromTokenAmount + ); + IERC20(_fromToken).universalApproveMax(approveTarget, fromTokenAmount); + } + + require(isWhiteListed[swapTarget], "DODOV1Proxy04: Not Whitelist Contract"); + (bool success, ) = swapTarget.call{value: _fromToken == _ETH_ADDRESS_ ? msg.value : 0}(callDataConcat); + + require(success, "DODOV1Proxy04: External Swap execution Failed"); + + IERC20(_toToken).universalTransfer( + msg.sender, + IERC20(_toToken).universalBalanceOf(address(this)) + ); + returnAmount = IERC20(_toToken).universalBalanceOf(msg.sender).sub(toTokenOriginBalance); + require(returnAmount >= minReturnAmount, "DODOV1Proxy04: Return amount is not enough"); + + emit OrderHistory(_fromToken, _toToken, msg.sender, fromTokenAmount, returnAmount); + + uint256 _gasExternalReturn = _GAS_EXTERNAL_RETURN_; + if(_gasExternalReturn > 0) { + if(gasleft() > 27710 + _gasExternalReturn * 6080) + IChi(_CHI_TOKEN_).freeUpTo(_gasExternalReturn); + } + } + + + function mixSwapV1( + address fromToken, + address toToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory mixPairs, + uint256[] memory directions, + address[] memory portionPath, + uint256 deadLine + ) external override payable judgeExpired(deadLine) returns (uint256 returnAmount) { + require(mixPairs.length == directions.length, "DODOV1Proxy04: PARAMS_LENGTH_NOT_MATCH"); + require(mixPairs.length > 0, "DODOV1Proxy04: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV1Proxy04: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV1Proxy04: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV1Proxy04: NOT_SUPPORT_BUY_CHI"); + + uint256 toTokenOriginBalance = IERC20(toToken).universalBalanceOf(msg.sender); + + if (fromToken != _ETH_ADDRESS_) { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( + fromToken, + msg.sender, + address(this), + fromTokenAmount + ); + } else { + require(msg.value == fromTokenAmount, "DODOV1Proxy04: ETH_AMOUNT_NOT_MATCH"); + IWETH(_WETH_).deposit{value: fromTokenAmount}(); + } + + for (uint256 i = 0; i < mixPairs.length; i++) { + address curPair = mixPairs[i]; + if (directions[i] == 0) { + address curDodoBase = IDODOV1(curPair)._BASE_TOKEN_(); + require(curDodoBase != _CHI_TOKEN_, "DODOV1Proxy04: NOT_SUPPORT_CHI"); + uint256 curAmountIn = IERC20(curDodoBase).balanceOf(address(this)); + IERC20(curDodoBase).universalApproveMax(curPair, curAmountIn); + IDODOV1(curPair).sellBaseToken(curAmountIn, 0, ""); + } else if(directions[i] == 1){ + address curDodoQuote = IDODOV1(curPair)._QUOTE_TOKEN_(); + require(curDodoQuote != _CHI_TOKEN_, "DODOV1Proxy04: NOT_SUPPORT_CHI"); + uint256 curAmountIn = IERC20(curDodoQuote).balanceOf(address(this)); + IERC20(curDodoQuote).universalApproveMax(curPair, curAmountIn); + uint256 canBuyBaseAmount = IDODOSellHelper(_DODO_SELL_HELPER_).querySellQuoteToken( + curPair, + curAmountIn + ); + IDODOV1(curPair).buyBaseToken(canBuyBaseAmount, curAmountIn, ""); + } else { + require(portionPath[0] != _CHI_TOKEN_, "DODOV1Proxy04: NOT_SUPPORT_CHI"); + uint256 curAmountIn = IERC20(portionPath[0]).balanceOf(address(this)); + IERC20(portionPath[0]).universalApproveMax(curPair, curAmountIn); + IUni(curPair).swapExactTokensForTokens(curAmountIn,0,portionPath,address(this),deadLine); + } + } + + IERC20(toToken).universalTransfer( + msg.sender, + IERC20(toToken).universalBalanceOf(address(this)) + ); + + returnAmount = IERC20(toToken).universalBalanceOf(msg.sender).sub(toTokenOriginBalance); + require(returnAmount >= minReturnAmount, "DODOV1Proxy04: Return amount is not enough"); + + emit OrderHistory(fromToken, toToken, msg.sender, fromTokenAmount, returnAmount); + + uint256 _gasExternalReturn = _GAS_EXTERNAL_RETURN_; + if(_gasExternalReturn > 0) { + if(gasleft() > 27710 + _gasExternalReturn * 6080) + IChi(_CHI_TOKEN_).freeUpTo(_gasExternalReturn); + } + } +} diff --git a/contracts/SmartRoute/DODOV2Proxy02.sol b/contracts/SmartRoute/DODOV2Proxy02.sol new file mode 100644 index 0000000..c301db4 --- /dev/null +++ b/contracts/SmartRoute/DODOV2Proxy02.sol @@ -0,0 +1,805 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {IDODOV2Proxy01} from "./intf/IDODOV2Proxy01.sol"; +import {IDODOV2} from "./intf/IDODOV2.sol"; +import {IDODOV1} from "./intf/IDODOV1.sol"; +import {IDODOApproveProxy} from "./DODOApproveProxy.sol"; +import {IDODOSellHelper} from "./helper/DODOSellHelper.sol"; +import {IERC20} from "../intf/IERC20.sol"; +import {IWETH} from "../intf/IWETH.sol"; +import {IUni} from "./intf/IUni.sol"; +import {IChi} from "./intf/IChi.sol"; +import {SafeMath} from "../lib/SafeMath.sol"; +import {UniversalERC20} from "./lib/UniversalERC20.sol"; +import {SafeERC20} from "../lib/SafeERC20.sol"; +import {DecimalMath} from "../lib/DecimalMath.sol"; +import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; +import {IDODOIncentive} from "../DODOToken/DODOIncentive.sol"; +import {IDODOAdapter} from "./intf/IDODOAdapter.sol"; + +/** + * @title DODOV2Proxy02 + * @author DODO Breeder + * + * @notice Entrance of trading in DODO platform + */ +contract DODOV2Proxy02 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable { + using SafeMath for uint256; + using UniversalERC20 for IERC20; + + // ============ Storage ============ + + address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public immutable _WETH_; + address public immutable _DODO_APPROVE_PROXY_; + address public immutable _DODO_SELL_HELPER_; + address public immutable _DVM_FACTORY_; + address public immutable _DPP_FACTORY_; + address public immutable _CP_FACTORY_; + address public immutable _DODO_INCENTIVE_; + address public immutable _CHI_TOKEN_; + uint256 public _GAS_DODO_MAX_RETURN_ = 0; + uint256 public _GAS_EXTERNAL_RETURN_ = 0; + mapping (address => bool) public isWhiteListed; + + // ============ Events ============ + + event OrderHistory( + address fromToken, + address toToken, + address sender, + uint256 fromAmount, + uint256 returnAmount + ); + + // ============ Modifiers ============ + + modifier judgeExpired(uint256 deadLine) { + require(deadLine >= block.timestamp, "DODOV2Proxy02: EXPIRED"); + _; + } + + fallback() external payable {} + + receive() external payable {} + + constructor( + address dvmFactory, + address dppFactory, + address cpFactory, + address payable weth, + address dodoApproveProxy, + address dodoSellHelper, + address chiToken, + address dodoIncentive + ) public { + _DVM_FACTORY_ = dvmFactory; + _DPP_FACTORY_ = dppFactory; + _CP_FACTORY_ = cpFactory; + _WETH_ = weth; + _DODO_APPROVE_PROXY_ = dodoApproveProxy; + _DODO_SELL_HELPER_ = dodoSellHelper; + _CHI_TOKEN_ = chiToken; + _DODO_INCENTIVE_ = dodoIncentive; + } + + function addWhiteList (address contractAddr) public onlyOwner { + isWhiteListed[contractAddr] = true; + } + + function removeWhiteList (address contractAddr) public onlyOwner { + isWhiteListed[contractAddr] = false; + } + + function updateGasReturn(uint256 newDodoGasReturn, uint256 newExternalGasReturn) public onlyOwner { + _GAS_DODO_MAX_RETURN_ = newDodoGasReturn; + _GAS_EXTERNAL_RETURN_ = newExternalGasReturn; + } + + // ============ DVM Functions (create & add liquidity) ============ + + function createDODOVendingMachine( + address baseToken, + address quoteToken, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 lpFeeRate, + uint256 i, + uint256 k, + bool isOpenTWAP, + uint256 deadLine + ) + external + override + payable + preventReentrant + judgeExpired(deadLine) + returns (address newVendingMachine, uint256 shares) + { + { + address _baseToken = baseToken == _ETH_ADDRESS_ ? _WETH_ : baseToken; + address _quoteToken = quoteToken == _ETH_ADDRESS_ ? _WETH_ : quoteToken; + newVendingMachine = IDODOV2(_DVM_FACTORY_).createDODOVendingMachine( + _baseToken, + _quoteToken, + lpFeeRate, + i, + k, + isOpenTWAP + ); + } + + { + address _baseToken = baseToken; + address _quoteToken = quoteToken; + _deposit( + msg.sender, + newVendingMachine, + _baseToken, + baseInAmount, + _baseToken == _ETH_ADDRESS_ + ); + _deposit( + msg.sender, + newVendingMachine, + _quoteToken, + quoteInAmount, + _quoteToken == _ETH_ADDRESS_ + ); + } + + (shares, , ) = IDODOV2(newVendingMachine).buyShares(msg.sender); + } + + function addDVMLiquidity( + address dvmAddress, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 baseMinAmount, + uint256 quoteMinAmount, + uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH + uint256 deadLine + ) + external + override + payable + preventReentrant + judgeExpired(deadLine) + returns ( + uint256 shares, + uint256 baseAdjustedInAmount, + uint256 quoteAdjustedInAmount + ) + { + address _dvm = dvmAddress; + (baseAdjustedInAmount, quoteAdjustedInAmount) = _addDVMLiquidity( + _dvm, + baseInAmount, + quoteInAmount + ); + require( + baseAdjustedInAmount >= baseMinAmount && quoteAdjustedInAmount >= quoteMinAmount, + "DODOV2Proxy02: deposit amount is not enough" + ); + + _deposit(msg.sender, _dvm, IDODOV2(_dvm)._BASE_TOKEN_(), baseAdjustedInAmount, flag == 1); + _deposit(msg.sender, _dvm, IDODOV2(_dvm)._QUOTE_TOKEN_(), quoteAdjustedInAmount, flag == 2); + + (shares, , ) = IDODOV2(_dvm).buyShares(msg.sender); + // refund dust eth + if (flag == 1 && msg.value > baseAdjustedInAmount) msg.sender.transfer(msg.value - baseAdjustedInAmount); + if (flag == 2 && msg.value > quoteAdjustedInAmount) msg.sender.transfer(msg.value - quoteAdjustedInAmount); + } + + function _addDVMLiquidity( + address dvmAddress, + uint256 baseInAmount, + uint256 quoteInAmount + ) internal view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount) { + (uint256 baseReserve, uint256 quoteReserve) = IDODOV2(dvmAddress).getVaultReserve(); + if (quoteReserve == 0 && baseReserve == 0) { + baseAdjustedInAmount = baseInAmount; + quoteAdjustedInAmount = quoteInAmount; + } + if (quoteReserve == 0 && baseReserve > 0) { + baseAdjustedInAmount = baseInAmount; + quoteAdjustedInAmount = 0; + } + if (quoteReserve > 0 && baseReserve > 0) { + uint256 baseIncreaseRatio = DecimalMath.divFloor(baseInAmount, baseReserve); + uint256 quoteIncreaseRatio = DecimalMath.divFloor(quoteInAmount, quoteReserve); + if (baseIncreaseRatio <= quoteIncreaseRatio) { + baseAdjustedInAmount = baseInAmount; + quoteAdjustedInAmount = DecimalMath.mulFloor(quoteReserve, baseIncreaseRatio); + } else { + quoteAdjustedInAmount = quoteInAmount; + baseAdjustedInAmount = DecimalMath.mulFloor(baseReserve, quoteIncreaseRatio); + } + } + } + + // ============ DPP Functions (create & reset) ============ + + function createDODOPrivatePool( + address baseToken, + address quoteToken, + uint256 baseInAmount, + uint256 quoteInAmount, + uint256 lpFeeRate, + uint256 i, + uint256 k, + bool isOpenTwap, + uint256 deadLine + ) + external + override + payable + preventReentrant + judgeExpired(deadLine) + returns (address newPrivatePool) + { + newPrivatePool = IDODOV2(_DPP_FACTORY_).createDODOPrivatePool(); + + address _baseToken = baseToken; + address _quoteToken = quoteToken; + _deposit(msg.sender, newPrivatePool, _baseToken, baseInAmount, _baseToken == _ETH_ADDRESS_); + _deposit( + msg.sender, + newPrivatePool, + _quoteToken, + quoteInAmount, + _quoteToken == _ETH_ADDRESS_ + ); + + if (_baseToken == _ETH_ADDRESS_) _baseToken = _WETH_; + if (_quoteToken == _ETH_ADDRESS_) _quoteToken = _WETH_; + + IDODOV2(_DPP_FACTORY_).initDODOPrivatePool( + newPrivatePool, + msg.sender, + _baseToken, + _quoteToken, + lpFeeRate, + k, + i, + isOpenTwap + ); + } + + function resetDODOPrivatePool( + address dppAddress, + uint256[] memory paramList, //0 - newLpFeeRate, 1 - newI, 2 - newK + uint256[] memory amountList, //0 - baseInAmount, 1 - quoteInAmount, 2 - baseOutAmount, 3- quoteOutAmount + uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH + uint256 minBaseReserve, + uint256 minQuoteReserve, + uint256 deadLine + ) external override payable preventReentrant judgeExpired(deadLine) { + _deposit( + msg.sender, + dppAddress, + IDODOV2(dppAddress)._BASE_TOKEN_(), + amountList[0], + flag == 1 + ); + _deposit( + msg.sender, + dppAddress, + IDODOV2(dppAddress)._QUOTE_TOKEN_(), + amountList[1], + flag == 2 + ); + + require(IDODOV2(IDODOV2(dppAddress)._OWNER_()).reset( + msg.sender, + paramList[0], + paramList[1], + paramList[2], + amountList[2], + amountList[3], + minBaseReserve, + minQuoteReserve + ), "Reset Failed"); + + _withdraw(msg.sender, IDODOV2(dppAddress)._BASE_TOKEN_(), amountList[2], flag == 3); + _withdraw(msg.sender, IDODOV2(dppAddress)._QUOTE_TOKEN_(), amountList[3], flag == 4); + } + + // ============ Swap ============ + + function dodoSwapV2ETHToToken( + address toToken, + uint256 minReturnAmount, + address[] memory dodoPairs, + uint256 directions, + bool isIncentive, + uint256 deadLine + ) + external + override + payable + judgeExpired(deadLine) + returns (uint256 returnAmount) + { + require(dodoPairs.length > 0, "DODOV2Proxy02: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV2Proxy02: RETURN_AMOUNT_ZERO"); + uint256 originGas = gasleft(); + + uint256 originToTokenBalance = IERC20(toToken).balanceOf(msg.sender); + IWETH(_WETH_).deposit{value: msg.value}(); + IWETH(_WETH_).transfer(dodoPairs[0], msg.value); + + for (uint256 i = 0; i < dodoPairs.length; i++) { + if (i == dodoPairs.length - 1) { + if (directions & 1 == 0) { + IDODOV2(dodoPairs[i]).sellBase(msg.sender); + } else { + IDODOV2(dodoPairs[i]).sellQuote(msg.sender); + } + } else { + if (directions & 1 == 0) { + IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]); + } else { + IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]); + } + } + directions = directions >> 1; + } + + returnAmount = IERC20(toToken).balanceOf(msg.sender).sub(originToTokenBalance); + require(returnAmount >= minReturnAmount, "DODOV2Proxy02: Return amount is not enough"); + + _dodoGasReturn(originGas); + + _execIncentive(isIncentive, _ETH_ADDRESS_, toToken); + + emit OrderHistory( + _ETH_ADDRESS_, + toToken, + msg.sender, + msg.value, + returnAmount + ); + } + + function dodoSwapV2TokenToETH( + address fromToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory dodoPairs, + uint256 directions, + bool isIncentive, + uint256 deadLine + ) + external + override + judgeExpired(deadLine) + returns (uint256 returnAmount) + { + require(dodoPairs.length > 0, "DODOV2Proxy02: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV2Proxy02: RETURN_AMOUNT_ZERO"); + uint256 originGas = gasleft(); + + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(fromToken, msg.sender, dodoPairs[0], fromTokenAmount); + + for (uint256 i = 0; i < dodoPairs.length; i++) { + if (i == dodoPairs.length - 1) { + if (directions & 1 == 0) { + IDODOV2(dodoPairs[i]).sellBase(address(this)); + } else { + IDODOV2(dodoPairs[i]).sellQuote(address(this)); + } + } else { + if (directions & 1 == 0) { + IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]); + } else { + IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]); + } + } + directions = directions >> 1; + } + returnAmount = IWETH(_WETH_).balanceOf(address(this)); + require(returnAmount >= minReturnAmount, "DODOV2Proxy02: Return amount is not enough"); + IWETH(_WETH_).withdraw(returnAmount); + msg.sender.transfer(returnAmount); + + _dodoGasReturn(originGas); + + _execIncentive(isIncentive, fromToken, _ETH_ADDRESS_); + + emit OrderHistory( + fromToken, + _ETH_ADDRESS_, + msg.sender, + fromTokenAmount, + returnAmount + ); + } + + function dodoSwapV2TokenToToken( + address fromToken, + address toToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory dodoPairs, + uint256 directions, + bool isIncentive, + uint256 deadLine + ) + external + override + judgeExpired(deadLine) + returns (uint256 returnAmount) + { + require(dodoPairs.length > 0, "DODOV2Proxy02: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV2Proxy02: RETURN_AMOUNT_ZERO"); + uint256 originGas = gasleft(); + + uint256 originToTokenBalance = IERC20(toToken).balanceOf(msg.sender); + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(fromToken, msg.sender, dodoPairs[0], fromTokenAmount); + + for (uint256 i = 0; i < dodoPairs.length; i++) { + if (i == dodoPairs.length - 1) { + if (directions & 1 == 0) { + IDODOV2(dodoPairs[i]).sellBase(msg.sender); + } else { + IDODOV2(dodoPairs[i]).sellQuote(msg.sender); + } + } else { + if (directions& 1 == 0) { + IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]); + } else { + IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]); + } + } + directions = directions >> 1; + } + returnAmount = IERC20(toToken).balanceOf(msg.sender).sub(originToTokenBalance); + require(returnAmount >= minReturnAmount, "DODOV2Proxy02: Return amount is not enough"); + + _dodoGasReturn(originGas); + + _execIncentive(isIncentive, fromToken, toToken); + + emit OrderHistory( + fromToken, + toToken, + msg.sender, + fromTokenAmount, + returnAmount + ); + } + + function externalSwap( + address fromToken, + address toToken, + address approveTarget, + address swapTarget, + uint256 fromTokenAmount, + uint256 minReturnAmount, + bytes memory callDataConcat, + bool isIncentive, + uint256 deadLine + ) + external + override + payable + judgeExpired(deadLine) + returns (uint256 returnAmount) + { + require(minReturnAmount > 0, "DODOV2Proxy02: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_BUY_CHI"); + + uint256 toTokenOriginBalance = IERC20(toToken).universalBalanceOf(msg.sender); + if (fromToken != _ETH_ADDRESS_) { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( + fromToken, + msg.sender, + address(this), + fromTokenAmount + ); + IERC20(fromToken).universalApproveMax(approveTarget, fromTokenAmount); + } + + require(isWhiteListed[swapTarget], "DODOV2Proxy02: Not Whitelist Contract"); + (bool success, ) = swapTarget.call{value: fromToken == _ETH_ADDRESS_ ? msg.value : 0}(callDataConcat); + + require(success, "DODOV2Proxy02: External Swap execution Failed"); + + IERC20(toToken).universalTransfer( + msg.sender, + IERC20(toToken).universalBalanceOf(address(this)) + ); + + returnAmount = IERC20(toToken).universalBalanceOf(msg.sender).sub(toTokenOriginBalance); + require(returnAmount >= minReturnAmount, "DODOV2Proxy02: Return amount is not enough"); + + _externalGasReturn(); + + _execIncentive(isIncentive, fromToken, toToken); + + emit OrderHistory( + fromToken, + toToken, + msg.sender, + fromTokenAmount, + returnAmount + ); + } + + function dodoSwapV1( + address fromToken, + address toToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory dodoPairs, + uint256 directions, + bool isIncentive, + uint256 deadLine + ) + external + override + payable + judgeExpired(deadLine) + returns (uint256 returnAmount) + { + require(dodoPairs.length > 0, "DODOV2Proxy02: PAIRS_EMPTY"); + require(minReturnAmount > 0, "DODOV2Proxy02: RETURN_AMOUNT_ZERO"); + require(fromToken != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_SELL_CHI"); + require(toToken != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_BUY_CHI"); + + uint256 originGas = gasleft(); + + address _fromToken = fromToken; + address _toToken = toToken; + + _deposit(msg.sender, address(this), _fromToken, fromTokenAmount, _fromToken == _ETH_ADDRESS_); + + for (uint256 i = 0; i < dodoPairs.length; i++) { + address curDodoPair = dodoPairs[i]; + if (directions & 1 == 0) { + address curDodoBase = IDODOV1(curDodoPair)._BASE_TOKEN_(); + require(curDodoBase != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_CHI"); + uint256 curAmountIn = IERC20(curDodoBase).balanceOf(address(this)); + IERC20(curDodoBase).universalApproveMax(curDodoPair, curAmountIn); + IDODOV1(curDodoPair).sellBaseToken(curAmountIn, 0, ""); + } else { + address curDodoQuote = IDODOV1(curDodoPair)._QUOTE_TOKEN_(); + require(curDodoQuote != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_CHI"); + uint256 curAmountIn = IERC20(curDodoQuote).balanceOf(address(this)); + IERC20(curDodoQuote).universalApproveMax(curDodoPair, curAmountIn); + uint256 canBuyBaseAmount = IDODOSellHelper(_DODO_SELL_HELPER_).querySellQuoteToken( + curDodoPair, + curAmountIn + ); + IDODOV1(curDodoPair).buyBaseToken(canBuyBaseAmount, curAmountIn, ""); + } + directions = directions >> 1; + } + + + if (_toToken == _ETH_ADDRESS_) { + returnAmount = IWETH(_WETH_).balanceOf(address(this)); + IWETH(_WETH_).withdraw(returnAmount); + } else { + returnAmount = IERC20(_toToken).tokenBalanceOf(address(this)); + } + + require(returnAmount >= minReturnAmount, "DODOV2Proxy02: Return amount is not enough"); + IERC20(_toToken).universalTransfer(msg.sender, returnAmount); + + _dodoGasReturn(originGas); + + _execIncentive(isIncentive, _fromToken, _toToken); + + emit OrderHistory(_fromToken, _toToken, msg.sender, fromTokenAmount, returnAmount); + } + + + function mixSwap( + address fromToken, + address toToken, + uint256 fromTokenAmount, + uint256 minReturnAmount, + address[] memory mixAdapters, + address[] memory mixPairs, + address[] memory assetTo, + uint256 directions, + bool isIncentive, + uint256 deadLine + ) external override payable judgeExpired(deadLine) returns (uint256 returnAmount) { + require(mixPairs.length > 0, "DODOV2Proxy02: PAIRS_EMPTY"); + require(mixPairs.length == mixAdapters.length, "DODOV2Proxy02: PAIR_ADAPTER_NOT_MATCH"); + require(mixPairs.length == assetTo.length - 1, "DODOV2Proxy02: PAIR_ASSETTO_NOT_MATCH"); + require(minReturnAmount > 0, "DODOV2Proxy02: RETURN_AMOUNT_ZERO"); + + address _fromToken = fromToken; + address _toToken = toToken; + uint256 _fromTokenAmount = fromTokenAmount; + + require(_fromToken != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_SELL_CHI"); + require(_toToken != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_BUY_CHI"); + + uint256 originGas = gasleft(); + uint256 toTokenOriginBalance = IERC20(_toToken).universalBalanceOf(msg.sender); + + _deposit(msg.sender, assetTo[0], _fromToken, _fromTokenAmount, _fromToken == _ETH_ADDRESS_); + + for (uint256 i = 0; i < mixPairs.length; i++) { + if (directions & 1 == 0) { + IDODOAdapter(mixAdapters[i]).sellBase(assetTo[i + 1],mixPairs[i]); + } else { + IDODOAdapter(mixAdapters[i]).sellQuote(assetTo[i + 1],mixPairs[i]); + } + directions = directions >> 1; + } + + if(_toToken == _ETH_ADDRESS_) { + returnAmount = IWETH(_WETH_).balanceOf(address(this)); + IWETH(_WETH_).withdraw(returnAmount); + msg.sender.transfer(returnAmount); + }else { + returnAmount = IERC20(_toToken).tokenBalanceOf(msg.sender).sub(toTokenOriginBalance); + } + + require(returnAmount >= minReturnAmount, "DODOV2Proxy02: Return amount is not enough"); + + _dodoGasReturn(originGas); + + _execIncentive(isIncentive, _fromToken, _toToken); + + emit OrderHistory( + _fromToken, + _toToken, + msg.sender, + _fromTokenAmount, + returnAmount + ); + } + + //============ CrowdPooling Functions (create & bid) ============ + + function createCrowdPooling( + address baseToken, + address quoteToken, + uint256 baseInAmount, + uint256[] memory timeLine, + uint256[] memory valueList, + bool isOpenTWAP, + uint256 deadLine + ) external override payable preventReentrant judgeExpired(deadLine) returns (address payable newCrowdPooling) { + address _baseToken = baseToken; + address _quoteToken = quoteToken == _ETH_ADDRESS_ ? _WETH_ : quoteToken; + + newCrowdPooling = IDODOV2(_CP_FACTORY_).createCrowdPooling(); + + _deposit( + msg.sender, + newCrowdPooling, + _baseToken, + baseInAmount, + false + ); + + newCrowdPooling.transfer(msg.value); + + IDODOV2(_CP_FACTORY_).initCrowdPooling( + newCrowdPooling, + msg.sender, + _baseToken, + _quoteToken, + timeLine, + valueList, + isOpenTWAP + ); + } + + function bid( + address cpAddress, + uint256 quoteAmount, + uint8 flag, // 0 - ERC20, 1 - quoteInETH + uint256 deadLine + ) external override payable preventReentrant judgeExpired(deadLine) { + _deposit(msg.sender, cpAddress, IDODOV2(cpAddress)._QUOTE_TOKEN_(), quoteAmount, flag == 1); + IDODOV2(cpAddress).bid(msg.sender); + } + + + function addLiquidityToV1( + address pair, + uint256 baseAmount, + uint256 quoteAmount, + uint256 baseMinShares, + uint256 quoteMinShares, + uint8 flag, // 0 erc20 In 1 baseInETH 2 quoteIn ETH + uint256 deadLine + ) external override payable preventReentrant judgeExpired(deadLine) returns(uint256 baseShares, uint256 quoteShares) { + address _baseToken = IDODOV1(pair)._BASE_TOKEN_(); + address _quoteToken = IDODOV1(pair)._QUOTE_TOKEN_(); + + _deposit(msg.sender, address(this), _baseToken, baseAmount, flag == 1); + _deposit(msg.sender, address(this), _quoteToken, quoteAmount, flag == 2); + + + if(baseAmount > 0) { + IERC20(_baseToken).universalApproveMax(pair, baseAmount); + baseShares = IDODOV1(pair).depositBaseTo(msg.sender, baseAmount); + } + if(quoteAmount > 0) { + IERC20(_quoteToken).universalApproveMax(pair, quoteAmount); + quoteShares = IDODOV1(pair).depositQuoteTo(msg.sender, quoteAmount); + } + + require(baseShares >= baseMinShares && quoteShares >= quoteMinShares,"DODOV2Proxy02: Return DLP is not enough"); + } + + + function _deposit( + address from, + address to, + address token, + uint256 amount, + bool isETH + ) internal { + if (isETH) { + if (amount > 0) { + IWETH(_WETH_).deposit{value: amount}(); + if (to != address(this)) SafeERC20.safeTransfer(IERC20(_WETH_), to, amount); + } + } else { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(token, from, to, amount); + } + } + + function _withdraw( + address payable to, + address token, + uint256 amount, + bool isETH + ) internal { + if (isETH) { + if (amount > 0) { + IWETH(_WETH_).withdraw(amount); + to.transfer(amount); + } + } else { + if (amount > 0) { + SafeERC20.safeTransfer(IERC20(token), to, amount); + } + } + } + + function _dodoGasReturn(uint256 originGas) internal { + uint256 _gasDodoMaxReturn = _GAS_DODO_MAX_RETURN_; + if(_gasDodoMaxReturn > 0) { + uint256 calcGasTokenBurn = originGas.sub(gasleft()) / 65000; + uint256 gasTokenBurn = calcGasTokenBurn > _gasDodoMaxReturn ? _gasDodoMaxReturn : calcGasTokenBurn; + if(gasTokenBurn >= 3 && gasleft() > 27710 + gasTokenBurn * 6080) + IChi(_CHI_TOKEN_).freeUpTo(gasTokenBurn); + } + } + + function _externalGasReturn() internal { + uint256 _gasExternalReturn = _GAS_EXTERNAL_RETURN_; + if(_gasExternalReturn > 0) { + if(gasleft() > 27710 + _gasExternalReturn * 6080) + IChi(_CHI_TOKEN_).freeUpTo(_gasExternalReturn); + } + } + + function _execIncentive(bool isIncentive, address fromToken,address toToken) internal { + if(isIncentive && gasleft() > 30000) { + IDODOIncentive(_DODO_INCENTIVE_).triggerIncentive(fromToken, toToken, msg.sender); + } + } + +} diff --git a/contracts/SmartRoute/adapter/DODOV1Adapter.sol b/contracts/SmartRoute/adapter/DODOV1Adapter.sol index 747571a..b8a07bd 100644 --- a/contracts/SmartRoute/adapter/DODOV1Adapter.sol +++ b/contracts/SmartRoute/adapter/DODOV1Adapter.sol @@ -9,6 +9,7 @@ pragma solidity 0.6.9; import {IERC20} from "../../intf/IERC20.sol"; import {IDODOV1} from "../intf/IDODOV1.sol"; +import {SafeERC20} from "../../lib/SafeERC20.sol"; import {IDODOSellHelper} from "../helper/DODOSellHelper.sol"; import {UniversalERC20} from "../lib/UniversalERC20.sol"; import {SafeMath} from "../../lib/SafeMath.sol"; @@ -26,18 +27,18 @@ contract DODOV1Adapter is IDODOAdapter { function sellBase(address to, address pool) external override { address curBase = IDODOV1(pool)._BASE_TOKEN_(); - uint256 curAmountIn = IERC20(curBase).balanceOf(address(this)); + uint256 curAmountIn = IERC20(curBase).tokenBalanceOf(address(this)); IERC20(curBase).universalApproveMax(pool, curAmountIn); IDODOV1(pool).sellBaseToken(curAmountIn, 0, ""); if(to != address(this)) { address curQuote = IDODOV1(pool)._QUOTE_TOKEN_(); - IERC20(curQuote).transfer(to,IERC20(curQuote).balanceOf(address(this))); + SafeERC20.safeTransfer(IERC20(curQuote), to, IERC20(curQuote).tokenBalanceOf(address(this))); } } function sellQuote(address to, address pool) external override { address curQuote = IDODOV1(pool)._QUOTE_TOKEN_(); - uint256 curAmountIn = IERC20(curQuote).balanceOf(address(this)); + uint256 curAmountIn = IERC20(curQuote).tokenBalanceOf(address(this)); IERC20(curQuote).universalApproveMax(pool, curAmountIn); uint256 canBuyBaseAmount = IDODOSellHelper(_DODO_SELL_HELPER_).querySellQuoteToken( pool, @@ -46,7 +47,7 @@ contract DODOV1Adapter is IDODOAdapter { IDODOV1(pool).buyBaseToken(canBuyBaseAmount, curAmountIn, ""); if(to != address(this)) { address curBase = IDODOV1(pool)._BASE_TOKEN_(); - IERC20(curBase).transfer(to,IERC20(curBase).balanceOf(address(this))); + SafeERC20.safeTransfer(IERC20(curBase), to, canBuyBaseAmount); } } } \ No newline at end of file diff --git a/deploy-detail-v1.5.txt b/deploy-detail-v1.5.txt index 9d412e0..10ef18c 100644 --- a/deploy-detail-v1.5.txt +++ b/deploy-detail-v1.5.txt @@ -100,3 +100,15 @@ Deploy time: 2021/1/20 上午10:58:55 Deploy type: Proxy DODOV1Proxy03 Address: 0x9A9942458754bDf65dCbCd0B6B4B842a7D4031AB Set DODOProxy Owner tx: 0x2fd5f1abb016536e4557d609bdf52e2b790cbac85dead5097128e77b4357252d +==================================================== +network type: bsclive +Deploy time: 2021/1/26 下午5:28:55 +Deploy type: Proxy +DODOV1Proxy04 Address: 0xbe9a66e49503e84ae59a4d0545365AABedf33b40 +Set DODOProxy Owner tx: 0xe5dd7a963446bb6a826c89a0c987baf0db463a649f3949fce6ef39b4d9649808 +==================================================== +network type: live +Deploy time: 2021/1/26 下午5:41:41 +Deploy type: Proxy +DODOV1Proxy04 Address: 0xa2CB66EBB947D217f61510882096F6e95c1DE97D +Set DODOProxy Owner tx: 0xe49234c67bc710a7f4797aacbb2ef26fac458832f101e856d8113cc21721cd81 diff --git a/deploy-detail-v2.0.txt b/deploy-detail-v2.0.txt index baff773..83b513d 100644 --- a/deploy-detail-v2.0.txt +++ b/deploy-detail-v2.0.txt @@ -393,15 +393,6 @@ Deploy time: 2021/1/22 下午12:12:05 Deploy type: V2 - Callee DODOCalleeHelperAddress: 0x8085e9a80edb737a08c158bd018683c7Ad3c082B ==================================================== -network type: bsclive -Deploy time: 2021/1/22 下午1:51:45 -==================================================== -network type: bsclive -Deploy time: 2021/1/22 下午1:54:22 -==================================================== -network type: bsclive -Deploy time: 2021/1/22 下午1:57:54 -==================================================== network type: kovan Deploy time: 2021/1/22 下午3:53:35 Deploy type: V2 @@ -436,7 +427,7 @@ DefaultMtFeeRateAddress: 0x18DFdE99F578A0735410797e949E8D3e2AFCB9D2 DefaultPermissionAddress: 0x729f7f44bf64Ce814716b6261e267DbE6cdf021c Init DefaultPermissionAddress Tx: 0x2fb909748e2afcc040becccae128256d264cd56f713329b49742255b9e478a2c DvmTemplateAddress: 0xC3BeD579CaB3EC29B22D9AB99F4E586af42496b9 -DppTemplateAddress: 0x550B2e7bD9605b8dcdd20d01bA73f1feB6ce289b +DppTemplateAddress: 0x85351262f7474Ebe23FfAcD633cf20A491F1325D DppAdminTemplateAddress: 0x989DcAA95801C527C5B73AA65d3962dF9aCe1b0C CpTemplateAddress: 0x041ABa00c57Dd47abC37A2931dF569a2A2cc57Be DODOApprove Address: 0x72d220cE168C4f361dD4deE5D826a01AD8598f6C @@ -462,7 +453,7 @@ DefaultMtFeeRateAddress: 0x5e84190a270333aCe5B9202a3F4ceBf11b81bB01 DefaultPermissionAddress: 0x6B208E08dcF6BD51F50C5Da09d15B2D8E5C46Cf2 Init DefaultPermissionAddress Tx: 0x235d8c2a5eea61edc73d4e7de1f13f7a4d0479935e4a72c7d400d1844a141071 DvmTemplateAddress: 0x2BBD66fC4898242BDBD2583BBe1d76E8b8f71445 -DppTemplateAddress: 0x18DFdE99F578A0735410797e949E8D3e2AFCB9D2 +DppTemplateAddress: 0xB76de21f04F677f07D9881174a1D8E624276314C DppAdminTemplateAddress: 0x729f7f44bf64Ce814716b6261e267DbE6cdf021c CpTemplateAddress: 0x18b0bD918b55f995Fd404B872404378A62cb403b DODOApprove Address: 0xC3BeD579CaB3EC29B22D9AB99F4E586af42496b9 @@ -485,3 +476,69 @@ Deploy type: V2 - Adapter DODOV1Adapter Address: 0xb57Dd5c265dBb13CA014F2332069E90CD0e22e65 DODOV2Adapter Address: 0xE55154D09265b18aC7CDAC6E646672A5460389a1 UniAdapter Address: 0x043957f7554275b90c5178872faE851dcfC1089D +==================================================== +network type: live +Deploy time: 2021/1/24 下午5:23:15 +Deploy type: V2 - ERC20 Factory +ERC20TemplateAddress: 0x85351262f7474Ebe23FfAcD633cf20A491F1325D +MintableERC20TemplateAddress: 0x0596908263Ef2724fBfBcAfA1c983FCD7a629038 +ERC20FactoryAddress: 0x44D5dF24d5Ef52A791D6436Fa45A8D426f6de34e +==================================================== +network type: live +Deploy time: 2021/1/25 下午7:40:34 +Deploy type: V2 - Adapter +DODOV1Adapter Address: 0x9B64c81ba54eA51e1f6B7fefb3cfF8AA6F1e2A09 +==================================================== +network type: bsclive +Deploy time: 2021/1/25 下午7:51:54 +Deploy type: V2 - Adapter +DODOV1Adapter Address: 0x0596908263Ef2724fBfBcAfA1c983FCD7a629038 +==================================================== +network type: kovan +Deploy time: 2021/1/25 下午7:54:22 +Deploy type: V2 - Adapter +DODOV1Adapter Address: 0xfbA45094b82AAdA3297712073Ee0920b4090cd7c +==================================================== +network type: kovan +Deploy time: 2021/1/26 上午11:02:21 +Deploy type: V2 +DppAdminTemplateAddress: 0x10AAaeE8B802FaEBEC5dE37dc581490d86Fb07A5 +DODOApprove Address: 0x8acF28D9d8124B20b645893b6102950B488dfd29 +DODOApproveProxy Address: 0x7Dac4ec78D4349FBD9E031a56E92B724B8AFA9B3 +DppFactoryAddress: 0x6DAb26dFE83E484DCC5126F812E3e6AA8e7eEf4D +Init DppFactory Tx: 0x019975d4e881fc929ab8c7f4ecfcec4f54785df20f369848c34ad07462a3fe27 +DODOV2RouteHelper Address: 0xE2dbE9CF7bEb0484F464281D2DcbF6bF98D865Fd +DODOV2Proxy02 Address: 0x3457A15B9ab57FC754789EE83E4BD2BD8f4F50C8 +Init DODOProxyV2 Tx: 0xd888ac726780d59c0902a3d2e0b1d057d740b6d0113a9b574b6e602aaae9c36c +DODOApproveProxy Init tx: 0x269cca5af1c412592439ddce1ff4d4963c5f361251a0e24437f6478a05da9cd9 +DODOApprove Init tx: 0x8883928f79a11fdf1c96a07d2a6c3c8034e1bcf5cfea87342fb5084cc22eb406 +DODOIncentive ChangeProxy tx: 0x1b01057283954629021d23586ba04278c64ada46c899f62b1f53364546d0fa88 +DODOIncentive OpenSwitch tx: 0x336c69f91d6f0b547e432498d3848f8308fe3164925444aca98951876e68a89c +==================================================== +network type: bsclive +Deploy time: 2021/1/26 下午4:25:33 +Deploy type: V2 +DppAdminTemplateAddress: 0x44D5dF24d5Ef52A791D6436Fa45A8D426f6de34e +DODOApproveProxy Address: 0xB76de21f04F677f07D9881174a1D8E624276314C +DppFactoryAddress: 0x9B64c81ba54eA51e1f6B7fefb3cfF8AA6F1e2A09 +Init DppFactory Tx: 0xf2037cb6bbf6b6fccf524e21af55e76f54edf8ae08e5c99e9310afa9192d75e1 +DODOV2RouteHelper Address: 0x335aC99bb3E51BDbF22025f092Ebc1Cf2c5cC619 +DODOV2Proxy02 Address: 0x6B4Fa0bc61Eddc928e0Df9c7f01e407BfcD3e5EF +Init DODOProxyV2 Tx: 0xb30622bdf50144d11d25b0c7c267270f6c6ef6a6eb89efee97f2d31f6e7986e6 +==================================================== +network type: live +Deploy time: 2021/1/26 下午5:07:20 +Deploy type: V2 +DppAdminTemplateAddress: 0x5515363c0412AdD5c72d3E302fE1bD7dCBCF93Fe +DODOApproveProxy Address: 0x335aC99bb3E51BDbF22025f092Ebc1Cf2c5cC619 +DppFactoryAddress: 0x6B4Fa0bc61Eddc928e0Df9c7f01e407BfcD3e5EF +Init DppFactory Tx: 0x3f0ada4251c74ba5f793c1d653a9be7818674daac19291619ce7b5defd232240 +DODOV2RouteHelper Address: 0xbe9a66e49503e84ae59a4d0545365AABedf33b40 +DODOV2Proxy02 Address: 0xa356867fDCEa8e71AEaF87805808803806231FdC +Init DODOProxyV2 Tx: 0xf8e511f50c2f27836ace72e65d8a10e6253f5d5094a9e1f2e5fa5181d3c05f08 +==================================================== +network type: bsclive +Deploy time: 2021/1/26 下午5:29:51 +==================================================== +network type: live +Deploy time: 2021/1/26 下午5:42:23 diff --git a/kovan-mock-v2.0.txt b/kovan-mock-v2.0.txt index 56847a1..20adee7 100644 --- a/kovan-mock-v2.0.txt +++ b/kovan-mock-v2.0.txt @@ -430,3 +430,15 @@ Create DPP: 0xd8C30a4E866B188F16aD266dC3333BD47F34ebaE-0x43688f367eb83697c3ca5d0 Create DPP: 0xd8C30a4E866B188F16aD266dC3333BD47F34ebaE-0x156595bAF85D5C29E91d959889B022d952190A64 Pool:0x67841b1553Ed349111659E56897Cd8480DA88f8c Tx: 0x165e682bdc696a6af5821944718508dfd7fc953f1e21e213a741d52ced53ab03 Create DPP: 0xd7f02D1b4F9495B549787808503Ecfd231C3fbDA-0x43688f367eb83697c3ca5d03c5055b6bd6f6ac4b Pool:0x3e8DB146423f2Df1Bb3f07C9D7Cf3e76D2C74446 Tx: 0x090746636f90cda0f12435e8cb29d325eb1c730e87e9b8746455b4071fcc2a9f Create DPP: 0xd7f02D1b4F9495B549787808503Ecfd231C3fbDA-0x156595bAF85D5C29E91d959889B022d952190A64 Pool:0xbD0CCdbC8FECcc68934Bf884fBe85AB7B51A15eb Tx: 0xdbda4038d439060f9ef3fc09df58f80758d38a81e246cbbc0c88a6b7c275b2ed +==================================================== +network type: kovan +Deploy time: 2021/1/26 上午11:13:49 +Mock POOL Tx: V2 +Approve:0xd8C30a4E866B188F16aD266dC3333BD47F34ebaE Tx: 0x21319fc7b2010f495706d835fe0d20177a51c4aa9e787ad4efb151f4a83b0b97 +Approve:0xd7f02D1b4F9495B549787808503Ecfd231C3fbDA Tx: 0x0219e34cf6be4d9f1384034ce4c35bc030f67d00e9e380b4b0acec64ecd553c1 +Approve:0x43688f367eb83697c3ca5d03c5055b6bd6f6ac4b Tx: 0x2c0a25770e0cc6a48d63a8c8f9067213695c8c1b657326379b58b6090d4c7b13 +Approve:0x156595bAF85D5C29E91d959889B022d952190A64 Tx: 0xe0b9870564d2037c75b3f345a9dd93d80d9405a949094459c5b34f94acd3ac61 +Create DPP: 0xd8C30a4E866B188F16aD266dC3333BD47F34ebaE-0x43688f367eb83697c3ca5d03c5055b6bd6f6ac4b Pool:0xF2b06A3A58CEd368ebc6e5BB578503ECC9d50043 Tx: 0x4671da84177eb8a69c2cce7ce42101f3e2f0cfc221100b5613d0a0d5565e8c79 +Create DPP: 0xd8C30a4E866B188F16aD266dC3333BD47F34ebaE-0x156595bAF85D5C29E91d959889B022d952190A64 Pool:0x1D690066fc3F2D808555839B630a89da118B9466 Tx: 0x5e3609531f6a6918bcc2f9d91b154d78f0b15d8b229da00cda960a380fd909dd +Create DPP: 0xd7f02D1b4F9495B549787808503Ecfd231C3fbDA-0x43688f367eb83697c3ca5d03c5055b6bd6f6ac4b Pool:0x7B0d1D528a08619B370eb5c920298F74465b1982 Tx: 0x1dffacc99a0ce542425eb5fd977da570dae26246abb929e2a4eb04fcdee6a994 +Create DPP: 0xd7f02D1b4F9495B549787808503Ecfd231C3fbDA-0x156595bAF85D5C29E91d959889B022d952190A64 Pool:0x3997576b46e26D3fCEa7C4A76a62146AE5A903F3 Tx: 0x07dc666596b9c47522ed8dfb194cfcc9d673c0c63ec4d456d7b7485ea7eca57b diff --git a/migrations/2_deploy_v1.5.js b/migrations/2_deploy_v1.5.js index 3004e16..14a024e 100644 --- a/migrations/2_deploy_v1.5.js +++ b/migrations/2_deploy_v1.5.js @@ -3,15 +3,15 @@ const { deploySwitch } = require('../truffle-config.js') const file = fs.createWriteStream("../deploy-detail-v1.5.txt", { 'flags': 'a' }); let logger = new console.Console(file, file); -const DODOApprove = artifacts.require("DODOApprove"); -const DODOV1Proxy03 = artifacts.require("DODOV1Proxy03"); +const DODOApproveProxy = artifacts.require("DODOApproveProxy"); +const DODOV1Proxy04 = artifacts.require("DODOV1Proxy04"); const DODOSellHelper = artifacts.require("DODOSellHelper"); const DODOSwapCalcHelper = artifacts.require("DODOSwapCalcHelper"); module.exports = async (deployer, network, accounts) => { let DODOSellHelperAddress = ""; let WETHAddress = ""; - let DODOApproveAddress = ""; + let DODOApproveProxyAddress = ""; let chiAddress = ""; let DODOSwapCalcHelperAddress = ""; let ownerAddress = "" @@ -19,20 +19,20 @@ module.exports = async (deployer, network, accounts) => { DODOSellHelperAddress = "0xbdEae617F2616b45DCB69B287D52940a76035Fe3"; WETHAddress = "0x5eca15b12d959dfcf9c71c59f8b467eb8c6efd0b"; DODOSwapCalcHelperAddress = "0x0473FFd7039435F1FC794281F2a05830A1a0108a"; - DODOApproveAddress = ""; + DODOApproveProxyAddress = ""; chiAddress = "0x0000000000004946c0e9f43f4dee607b0ef1fa1c"; ownerAddress = accounts[0]; } else if (network == "live") { DODOSellHelperAddress = "0x533da777aedce766ceae696bf90f8541a4ba80eb"; WETHAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; - DODOApproveAddress = "0xCB859eA579b28e02B87A1FDE08d087ab9dbE5149"; + DODOApproveProxyAddress = "0x335aC99bb3E51BDbF22025f092Ebc1Cf2c5cC619"; chiAddress = "0x0000000000004946c0e9F43F4Dee607b0eF1fA1c"; DODOSwapCalcHelperAddress = "0x3C02477f1B3C70D692be95a6e3805E02bba71206"; ownerAddress = "0x95C4F5b83aA70810D4f142d58e5F7242Bd891CB0"; } else if (network == "bsclive") { DODOSellHelperAddress = "0x0F859706AeE7FcF61D5A8939E8CB9dBB6c1EDA33"; WETHAddress = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"; - DODOApproveAddress = "0xa128Ba44B2738A558A1fdC06d6303d52D3Cef8c1"; + DODOApproveProxyAddress = "0xB76de21f04F677f07D9881174a1D8E624276314C"; chiAddress = "0x0000000000000000000000000000000000000000"; DODOSwapCalcHelperAddress = "0xb0199C2c8ADF1E6c1e41De60A62E993406Cb8C02"; ownerAddress = "0x4073f2b9bB95774531b9e23d206a308c614A943a"; @@ -46,10 +46,10 @@ module.exports = async (deployer, network, accounts) => { var tx; logger.log("Deploy type: Proxy"); - if (DODOApproveAddress == "") { - await deployer.deploy(DODOApprove); - DODOApproveAddress = DODOApprove.address; - logger.log("DODOApprove Address: ", DODOApproveAddress); + if (DODOApproveProxyAddress == "") { + await deployer.deploy(DODOApproveProxy); + DODOApproveProxyAddress = DODOApproveProxy.address; + logger.log("DODOApproveProxy Address: ", DODOApproveDODOApproveProxyAddressAddress); } if (DODOSellHelperAddress == "") { await deployer.deploy(DODOSellHelper); @@ -62,14 +62,14 @@ module.exports = async (deployer, network, accounts) => { } await deployer.deploy( - DODOV1Proxy03, - DODOApproveAddress, + DODOV1Proxy04, + DODOApproveProxyAddress, DODOSellHelperAddress, WETHAddress, chiAddress ); - logger.log("DODOV1Proxy03 Address: ", DODOV1Proxy03.address); - const DODOProxyInstance = await DODOV1Proxy03.at(DODOV1Proxy03.address); + logger.log("DODOV1Proxy04 Address: ", DODOV1Proxy04.address); + const DODOProxyInstance = await DODOV1Proxy04.at(DODOV1Proxy04.address); tx = await DODOProxyInstance.initOwner(ownerAddress); logger.log("Set DODOProxy Owner tx: ", tx.tx); diff --git a/migrations/3_deploy_v2.js b/migrations/3_deploy_v2.js index 4fdddcc..0013aef 100644 --- a/migrations/3_deploy_v2.js +++ b/migrations/3_deploy_v2.js @@ -12,12 +12,17 @@ const DppTemplate = artifacts.require("DPP"); const DppAdminTemplate = artifacts.require("DPPAdmin"); const CpTemplate = artifacts.require("CP"); +const ERC20Template = artifacts.require("InitializableERC20"); +const MintableERC20Template = artifacts.require("InitializableMintableERC20"); +const ERC20Factory = artifacts.require("ERC20Factory"); + const DvmFactory = artifacts.require("DVMFactory"); const DppFactory = artifacts.require("DPPFactory"); const CpFactory = artifacts.require("CrowdPoolingFactory"); const DODOApprove = artifacts.require("DODOApprove"); -const DODOProxyV2 = artifacts.require("DODOV2Proxy01"); +const DODOApproveProxy = artifacts.require("DODOApproveProxy"); +const DODOProxyV2 = artifacts.require("DODOV2Proxy02"); const DODOIncentive = artifacts.require("DODOIncentive"); const DODOSellHelper = artifacts.require("DODOSellHelper"); const DODOCalleeHelper = artifacts.require("DODOCalleeHelper"); @@ -52,6 +57,7 @@ module.exports = async (deployer, network, accounts) => { let CpFactoryAddress = ""; //Approve let DODOApproveAddress = ""; + let DODOApproveProxyAddress = ""; //Incentive let DODOIncentiveAddress = ""; let DODOTokenAddress = ""; @@ -59,31 +65,37 @@ module.exports = async (deployer, network, accounts) => { let multiSigAddress = ""; let defaultMaintainer = ""; + //ERC20 + let ERC20TemplateAddress = ""; + let MintableERC20TemplateAddress = ""; + let ERC20FactoryAddress = ""; + if (network == "kovan") { //Helper DODOSellHelperAddress = "0xbdEae617F2616b45DCB69B287D52940a76035Fe3"; WETHAddress = "0x5eca15b12d959dfcf9c71c59f8b467eb8c6efd0b"; chiAddress = "0x0000000000004946c0e9f43f4dee607b0ef1fa1c"; - DODOCalleeHelperAddress = ""; + DODOCalleeHelperAddress = "0xf45e57FE0c0Bf759E34152179B2dA0a4e1a6BC9B"; DODOV1PmmHelperAddress = "0xC972069473a686b1c11Bd9347D719c87e6745d39"; DODORouteV2HelperAddress = ""; //Template CloneFactoryAddress = "0xf7959fe661124C49F96CF30Da33729201aEE1b27"; - DefaultMtFeeRateAddress = ""; + DefaultMtFeeRateAddress = "0x2F7e3B1c22C1baE2224Cef9F8BFe6B13789Fd0F7"; DefaultPermissionAddress = "0xACc7E23368261e1E02103c4e5ae672E7D01f5797"; - DvmTemplateAddress = ""; - DppTemplateAddress = ""; + DvmTemplateAddress = "0xA6384D1501842e9907D43148E2ca0d50E4ad56E2"; + DppTemplateAddress = "0x044b48D64E77Ab8854C46c8456dC05C540c9dd53"; DppAdminTemplateAddress = ""; - CpTemplateAddress = ""; + CpTemplateAddress = "0x81c802080c3CE0dE98fcb625670A14Eb8440184a"; //Factory - DvmFactoryAddress = ""; + DvmFactoryAddress = "0xE842d8c9A54B23C4D0cf208daCA3882c0c311353"; DppFactoryAddress = ""; - CpFactoryAddress = ""; + CpFactoryAddress = "0xD25e0A9A464f50191d9C879bE818FbA44680E980"; //Approve DODOApproveAddress = ""; - DODOIncentiveAddress = ""; + DODOApproveProxyAddress = ""; + DODOIncentiveAddress = "0x1f69E3CEAbDc464Ab11bceB15726530CD8AC535E"; DODOTokenAddress = "0xfF2985D13953Cb92ecc585aA2B6A4AF8cB46068f"; //Account multiSigAddress = accounts[0]; @@ -102,16 +114,17 @@ module.exports = async (deployer, network, accounts) => { DefaultPermissionAddress = "0x6B208E08dcF6BD51F50C5Da09d15B2D8E5C46Cf2"; DvmTemplateAddress = "0x2BBD66fC4898242BDBD2583BBe1d76E8b8f71445"; - DppTemplateAddress = "0x18DFdE99F578A0735410797e949E8D3e2AFCB9D2"; - DppAdminTemplateAddress = "0x729f7f44bf64Ce814716b6261e267DbE6cdf021c"; + DppTemplateAddress = "0xB76de21f04F677f07D9881174a1D8E624276314C"; + DppAdminTemplateAddress = ""; CpTemplateAddress = "0x18b0bD918b55f995Fd404B872404378A62cb403b"; //Factory - DvmFactoryAddress = ""; + DvmFactoryAddress = "0x72d220cE168C4f361dD4deE5D826a01AD8598f6C"; DppFactoryAddress = ""; - CpFactoryAddress = ""; + CpFactoryAddress = "0xE8C9A78725D0451FA19878D5f8A3dC0D55FECF25"; //Proxy - DODOApproveAddress = "0xC3BeD579CaB3EC29B22D9AB99F4E586af42496b9"; - DODOIncentiveAddress = ""; + DODOApproveAddress = "0xCB859eA579b28e02B87A1FDE08d087ab9dbE5149"; + DODOApproveProxyAddress = ""; + DODOIncentiveAddress = "0x989DcAA95801C527C5B73AA65d3962dF9aCe1b0C"; DODOTokenAddress = "0x43Dfc4159D86F3A37A5A4B3D4580b888ad7d4DDd"; //Account multiSigAddress = "0x95C4F5b83aA70810D4f142d58e5F7242Bd891CB0"; @@ -121,25 +134,26 @@ module.exports = async (deployer, network, accounts) => { DODOSellHelperAddress = "0x0F859706AeE7FcF61D5A8939E8CB9dBB6c1EDA33"; WETHAddress = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"; chiAddress = "0x0000000000000000000000000000000000000000"; - DODOCalleeHelperAddress = ""; + DODOCalleeHelperAddress = "0xDfaf9584F5d229A9DBE5978523317820A8897C5A"; DODORouteV2HelperAddress = ""; - DODOV1PmmHelperAddress = ""; + DODOV1PmmHelperAddress = "0x2BBD66fC4898242BDBD2583BBe1d76E8b8f71445"; //Template CloneFactoryAddress = "0x03E2427859119E497EB856a166F616a2Ce5f8c88"; - DefaultMtFeeRateAddress = ""; - DefaultPermissionAddress = ""; + DefaultMtFeeRateAddress = "0x18DFdE99F578A0735410797e949E8D3e2AFCB9D2"; + DefaultPermissionAddress = "0x729f7f44bf64Ce814716b6261e267DbE6cdf021c"; - DvmTemplateAddress = ""; - DppTemplateAddress = ""; + DvmTemplateAddress = "0xC3BeD579CaB3EC29B22D9AB99F4E586af42496b9"; + DppTemplateAddress = "0x85351262f7474Ebe23FfAcD633cf20A491F1325D"; DppAdminTemplateAddress = ""; - CpTemplateAddress = ""; + CpTemplateAddress = "0x041ABa00c57Dd47abC37A2931dF569a2A2cc57Be"; //Factory - DvmFactoryAddress = ""; + DvmFactoryAddress = "0xf50BDc9E90B7a1c138cb7935071b85c417C4cb8e"; DppFactoryAddress = ""; - CpFactoryAddress = ""; + CpFactoryAddress = "0x9aE501385Bc7996A2A4a1FBb00c8d3820611BCB5"; //Proxy - DODOApproveAddress = ""; - DODOIncentiveAddress = ""; + DODOApproveAddress = "0xa128Ba44B2738A558A1fdC06d6303d52D3Cef8c1"; + DODOApproveProxyAddress = ""; + DODOIncentiveAddress = "0x80930Cb1849F7D42531506fF45E66724338A821b"; DODOTokenAddress = "0x497A44c951fCCF92ADfdeD0a5b0162256F147647"; //Account multiSigAddress = "0x4073f2b9bB95774531b9e23d206a308c614A943a"; @@ -161,11 +175,29 @@ module.exports = async (deployer, network, accounts) => { logger.log("UniAdapter Address: ", UniAdapter.address); } - if (deploySwitch.CALLEE) { - logger.log("Deploy type: V2 - Callee"); - await deployer.deploy(DODOCalleeHelper, WETHAddress); - DODOCalleeHelperAddress = DODOCalleeHelper.address; - logger.log("DODOCalleeHelperAddress: ", DODOCalleeHelperAddress); + if (deploySwitch.ERC20) { + logger.log("Deploy type: V2 - ERC20 Factory"); + if (ERC20TemplateAddress == "") { + await deployer.deploy(ERC20Template); + ERC20TemplateAddress = ERC20Template.address; + logger.log("ERC20TemplateAddress: ", ERC20TemplateAddress); + } + if (MintableERC20TemplateAddress == "") { + await deployer.deploy(MintableERC20Template); + MintableERC20TemplateAddress = MintableERC20Template.address; + logger.log("MintableERC20TemplateAddress: ", MintableERC20TemplateAddress); + } + + if (ERC20FactoryAddress == "") { + await deployer.deploy( + ERC20Factory, + CloneFactoryAddress, + ERC20TemplateAddress, + MintableERC20TemplateAddress + ); + ERC20FactoryAddress = ERC20Factory.address; + logger.log("ERC20FactoryAddress: ", ERC20FactoryAddress); + } } if (deploySwitch.DEPLOY_V2) { @@ -239,6 +271,12 @@ module.exports = async (deployer, network, accounts) => { logger.log("DODOApprove Address: ", DODOApproveAddress); } + if (DODOApproveProxyAddress == "") { + await deployer.deploy(DODOApproveProxy, DODOApproveAddress); + DODOApproveProxyAddress = DODOApproveProxy.address; + logger.log("DODOApproveProxy Address: ", DODOApproveProxyAddress); + } + //Incentive if (DODOIncentiveAddress == "") { await deployer.deploy(DODOIncentive, DODOTokenAddress); @@ -273,7 +311,7 @@ module.exports = async (deployer, network, accounts) => { DppAdminTemplateAddress, defaultMaintainer, DefaultMtFeeRateAddress, - DODOApproveAddress + DODOApproveProxyAddress ); DppFactoryAddress = DppFactory.address; logger.log("DppFactoryAddress: ", DppFactoryAddress); @@ -312,24 +350,28 @@ module.exports = async (deployer, network, accounts) => { DppFactoryAddress, CpFactoryAddress, WETHAddress, - DODOApproveAddress, + DODOApproveProxyAddress, DODOSellHelperAddress, chiAddress, DODOIncentiveAddress ); - logger.log("DODOProxyV2 Address: ", DODOProxyV2.address); + logger.log("DODOV2Proxy02 Address: ", DODOProxyV2.address); const DODOProxyV2Instance = await DODOProxyV2.at(DODOProxyV2.address); var tx = await DODOProxyV2Instance.initOwner(multiSigAddress); logger.log("Init DODOProxyV2 Tx:", tx.tx); - const DODOApproveInstance = await DODOApprove.at(DODOApproveAddress); - var tx = await DODOApproveInstance.init(multiSigAddress, DODOProxyV2.address); - logger.log("DODOApprove Init tx: ", tx.tx); - - if (network == 'kovan') { - //1. Proxy whiteList + + const DODOApproveProxyInstance = await DODOApproveProxy.at(DODOApproveProxyAddress); + var tx = await DODOApproveProxyInstance.init(multiSigAddress, [DODOProxyV2.address]); + logger.log("DODOApproveProxy Init tx: ", tx.tx); + + + const DODOApproveInstance = await DODOApprove.at(DODOApproveAddress); + var tx = await DODOApproveInstance.init(multiSigAddress, DODOApproveProxy.address); + logger.log("DODOApprove Init tx: ", tx.tx); + //2. ChangeDODO Incentive proxy const DODOIncentiveInstance = await DODOIncentive.at(DODOIncentiveAddress); diff --git a/migrations/4_deploy_v2_mock.js b/migrations/4_deploy_v2_mock.js index 49a2d1b..f368b69 100644 --- a/migrations/4_deploy_v2_mock.js +++ b/migrations/4_deploy_v2_mock.js @@ -64,10 +64,10 @@ module.exports = async (deployer, network, accounts) => { let MintableERC20TemplateAddress = "0xA45a64DAba80757432fA4d654Df12f65f020C13C"; let ERC20FactoryAddress = "0xCb1A2f64EfB02803276BFB5a8D511C4D950282a0"; - let DPPFactoryAddress = "0xaFC7F3Ef4f8609411653FbF1Dd6D583A8Ae1f0fA"; + let DPPFactoryAddress = "0x6DAb26dFE83E484DCC5126F812E3e6AA8e7eEf4D"; let DVMFactoryAddress = "0xE842d8c9A54B23C4D0cf208daCA3882c0c311353"; - let DODOApproveAddress = "0x6FB6313cafaFe38acA19Be0B8151d6354F74A340"; - let DODOProxyV2Address = "0xfDEDc04A641DE9CEeb4eD2F24d98faa22418Bd94"; + let DODOApproveAddress = "0x8acF28D9d8124B20b645893b6102950B488dfd29"; + let DODOProxyV2Address = "0x3457A15B9ab57FC754789EE83E4BD2BD8f4F50C8"; @@ -193,22 +193,22 @@ module.exports = async (deployer, network, accounts) => { // const quoteInAmount = web3.utils.toWei("0.5", 'ether'); const deadline = Math.floor(new Date().getTime() / 1000 + 60 * 10); //DVM Pool - for (var i = 0; i < POOL_PARAM.length; i++) { - tx = await DODOProxyV2Instance.createDODOVendingMachine( - POOL_PARAM[i].baseAddr, - POOL_PARAM[i].quoteAddr, - baseInAmount, - 0, - POOL_PARAM[i].lpFeeRate, - POOL_PARAM[i].i, - POOL_PARAM[i].k, - false, - deadline - ); - var poolAddress = await DVMFactoryInstance._REGISTRY_(POOL_PARAM[i].baseAddr, POOL_PARAM[i].quoteAddr, 0); - logger.log("Create DVM: " + POOL_PARAM[i].baseAddr + "-" + POOL_PARAM[i].quoteAddr + " Pool:" + poolAddress + " Tx:", tx.tx); - } - //DVM Pool + // for (var i = 0; i < POOL_PARAM.length; i++) { + // tx = await DODOProxyV2Instance.createDODOVendingMachine( + // POOL_PARAM[i].baseAddr, + // POOL_PARAM[i].quoteAddr, + // baseInAmount, + // 0, + // POOL_PARAM[i].lpFeeRate, + // POOL_PARAM[i].i, + // POOL_PARAM[i].k, + // false, + // deadline + // ); + // var poolAddress = await DVMFactoryInstance._REGISTRY_(POOL_PARAM[i].baseAddr, POOL_PARAM[i].quoteAddr, 0); + // logger.log("Create DVM: " + POOL_PARAM[i].baseAddr + "-" + POOL_PARAM[i].quoteAddr + " Pool:" + poolAddress + " Tx:", tx.tx); + // } + //DPP Pool for (var i = 0; i < POOL_PARAM.length; i++) { tx = await DODOProxyV2Instance.createDODOPrivatePool( POOL_PARAM[i].baseAddr, diff --git a/test/utils/Contracts.ts b/test/utils/Contracts.ts index f5bd3e0..5842d33 100644 --- a/test/utils/Contracts.ts +++ b/test/utils/Contracts.ts @@ -35,11 +35,12 @@ export const DVM_PROXY_NAME = "DVMProxy" export const CONST_FEE_RATE_MODEL_NAME = "ConstFeeRateModel" export const PERMISSION_MANAGER_NAME = "PermissionManager" export const EXTERNAL_VALUE_NAME = "ExternalValue" -export const DODO_PROXY_NAME = "DODOV2Proxy01" +export const DODO_PROXY_NAME = "DODOV2Proxy02" export const FEE_RATE_MODEL_NAME = "FeeRateModel" export const DPP_NAME = "DPP" export const DPP_FACTORY_NAME = "DPPFactory" export const SMART_APPROVE = "DODOApprove" +export const SMART_APPROVE_PROXY = "DODOApproveProxy" export const DODO_SELL_HELPER = "DODOSellHelper" export const DPP_ADMIN_NAME = "DPPAdmin" export const DODO_CALLEE_HELPER_NAME = "DODOCalleeHelper" diff --git a/test/utils/ProxyContextV2.ts b/test/utils/ProxyContextV2.ts index 3d36d85..ada49e0 100644 --- a/test/utils/ProxyContextV2.ts +++ b/test/utils/ProxyContextV2.ts @@ -28,6 +28,7 @@ export class ProxyContext { DPPFactory: Contract; CPFactory: Contract; DODOApprove: Contract; + DODOApproveProxy: Contract; DODOCalleeHelper: Contract; DODOSellHelper: Contract; @@ -95,6 +96,11 @@ export class ProxyContext { contracts.SMART_APPROVE ); + this.DODOApproveProxy = await contracts.newContract( + contracts.SMART_APPROVE_PROXY, + [this.DODOApprove.options.address] + ) + //DODO Incentive this.DODOIncentive = await contracts.newContract( contracts.DODO_INCENTIVE, @@ -108,7 +114,7 @@ export class ProxyContext { dppAdminTemplate.options.address, this.Deployer, mtFeeRateModelTemplate.options.address, - this.DODOApprove.options.address + this.DODOApproveProxy.options.address ] ) @@ -134,7 +140,7 @@ export class ProxyContext { this.DPPFactory.options.address, this.CPFactory.options.address, this.WETH.options.address, - this.DODOApprove.options.address, + this.DODOApproveProxy.options.address, this.DODOSellHelper.options.address, "0x0000000000000000000000000000000000000000", this.DODOIncentive.options.address @@ -142,9 +148,10 @@ export class ProxyContext { ); await this.DODOProxyV2.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); - await this.DODOApprove.methods.init(this.Deployer,this.DODOProxyV2.options.address).send(this.sendParam(this.Deployer)); + await this.DODOApprove.methods.init(this.Deployer,this.DODOApproveProxy.options.address).send(this.sendParam(this.Deployer)); + await this.DODOApproveProxy.methods.init(this.Deployer, [this.DODOProxyV2.options.address]).send(this.sendParam(this.Deployer)); + await this.DODOIncentive.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); - await this.DODOIncentive.methods.changeDODOProxy(this.DODOProxyV2.options.address).send(this.sendParam(this.Deployer)); this.DODOCalleeHelper = await contracts.newContract( diff --git a/truffle-config.js b/truffle-config.js index 380aa46..5559589 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -38,15 +38,14 @@ module.exports = { * $ truffle test --network */ deploySwitch: { - DEPLOY_V1: false, + DEPLOY_V1: true, DEPLOY_V2: false, ADAPTER: false, MOCK_TOKEN: false, MOCK_V2_POOL: false, MOCK_V2_SWAP: false, MANUAL_ADD_POOL: false, - MOCK_TARGET_POOL: false, - CALLEE: false, + MOCK_TARGET_POOL: false }, networks: { From 9382e0f2ca55a8a8d31c209d1d8de3245176a6b8 Mon Sep 17 00:00:00 2001 From: mingda Date: Sat, 23 Jan 2021 18:34:22 +0800 Subject: [PATCH 09/42] update reademe --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 99ceea7..5dc7d77 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # DODO V2: Help 1 Trillion People Issue Token + ## Audit Report [Audited by Peckshield](https://github.com/DODOEX/contractV2/blob/main/audit/PeckShield-Audit-DODOV2-v1.0.pdf) @@ -32,3 +33,4 @@ Anyone who reports a unique, previously-unreported vulnerability that results in ## Contact Us Send E-mail to contact@dodoex.io + From c973b9bf053c7d47b761c0d55092a5a62d1ec585 Mon Sep 17 00:00:00 2001 From: owen05 Date: Sun, 31 Jan 2021 21:19:47 +0800 Subject: [PATCH 10/42] vDODO ing --- .../vDODOToken.sol} | 174 ++++++++++-------- 1 file changed, 97 insertions(+), 77 deletions(-) rename contracts/{VDODO/VDODOChef.sol => DODOToken/vDODOToken.sol} (77%) diff --git a/contracts/VDODO/VDODOChef.sol b/contracts/DODOToken/vDODOToken.sol similarity index 77% rename from contracts/VDODO/VDODOChef.sol rename to contracts/DODOToken/vDODOToken.sol index 5a7f4fc..6468515 100644 --- a/contracts/VDODO/VDODOChef.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -4,6 +4,8 @@ SPDX-License-Identifier: Apache-2.0 */ +pragma solidity 0.6.9; + import {IERC20} from "../intf/IERC20.sol"; import {Address} from "../lib/Address.sol"; import {SafeMath} from "../lib/SafeMath.sol"; @@ -12,46 +14,52 @@ import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; import {SafeERC20} from "../lib/SafeERC20.sol"; import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; -pragma solidity 0.6.9; + interface IGovernance { function governanceCall(address account, uint256 amount,bytes calldata data) external returns (bool); } -pragma solidity 0.6.9; -interface IHelper { - function getDodoAmount() external returns (uint256); + +interface IDODOLockedHelper { + function getDodoLockedAmount() external returns (uint256); } -pragma solidity 0.6.9; -contract VDODOToken is IERC20,InitializableOwnable ,ReentrancyGuard{ + + +contract vDODOToken is IERC20, InitializableOwnable ,ReentrancyGuard{ using SafeMath for uint256; using SafeERC20 for IERC20; - string private _name; - string private _symbol; - uint8 private _decimals; + // ============ Storage(ERC20) ============ + + string public name; + string public symbol; + uint8 public decimals; + uint256 public totalSupply; + mapping(address => mapping(address => uint256)) internal _ALLOWED_; + + + // ============ Storage ============ + address immutable _DODO_LOCKED_HELPER_; + address immutable _DODO_TOKEN_; + address immutable _DODO_APPROVE_PROXY_; + address public _DOOD_GOV_; bool cantransfer; - address govAddr; - address helperAddr; - IERC20 dodo; + uint256 public dodoPerBlock; - - uint256 public alpha = 100; - uint256 public totalVdodoAmount; - uint256 public totalOverdraft; - - uint256 public dodoPerBlock = 1e18;//TODO - uint256 public lastRewardBlock ; - uint256 public dodoFeeDestroyRatio ; uint256 constant public _MAG_SP_AMOUNT_ = 10; + //TODO: 可去掉 uint256 constant public _MAG_TOTALSP_AMOUNT_ = 110; uint256 constant public _BASE_AMOUNT_ = 100; - address constant public _DESTROY_ADDRESS_ = 0x0000000000000000000000000000000000000000; uint256 constant public _MIN_X_ = 1; uint256 constant public _MIN_X_Y_ = 5; uint256 constant public _MAX_X_ = 10; uint256 constant public _MAX_X_Y_ = 15; - + + uint256 public alpha = 100; + uint256 public totalOverDraft; + uint256 public lastRewardBlock; + uint256 public dodoFeeDestroyRatio; mapping(address => mapping(address => uint256)) internal _ALLOWED_; mapping(address => bool) public operater; mapping(address => UserInfo) public userInfo; @@ -64,54 +72,47 @@ contract VDODOToken is IERC20,InitializableOwnable ,ReentrancyGuard{ bool hasParticipateGov; //是否正在参与治理,是的话就不可以提币 } - // ============ Events ============ - event ParticipatoryGov(address indexed user, uint256 amount); - event Deposit(address indexed user,address indexed superior, uint256 amount); - event Redeem(address indexed user, uint256 amount); + event ParticipatoryGov(address user, uint256 amount); + event Deposit(address user,address superior, uint256 amount); + event Redeem(address user, uint256 amount); event SetCantransfer(bool allowed); - event RemoveOperation(address indexed operater); - event AddOperation(address indexed operater); + event RemoveOperation(address operater); + event AddOperation(address operater); event ChangePerReward(uint256 dodoPerBlock); event UpdateDodoFeeDestroyRatio(uint256 dodoFeeDestroyRatio); + event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); + + // ============ Modifiers ============ + //TODO: 是否需要operator的白名单设计? modifier onlyOperater() { - require(cantransfer || operater[msg.sender] , "not allowed transfer"); + require(cantransfer || operater[msg.sender] , "vDODOToken: not allowed transfer"); _; } + //TODO:是否可以不写? receive() external payable { revert(); } constructor( - address _govAddr, - address _dodo, - address _helperAddr, - string memory name, - string memory symbol) + address _dodoGov, + address _dodoToken, + address _dodoLockedHelper, + address _dodoApproveProxy, + string memory _name, + string memory _symbol) public { - _name = name; - _symbol = symbol; - _decimals = 18; - govAddr = _govAddr; - helperAddr = _helperAddr; - - dodo = IERC20(_dodo); - } - function name() public view override returns (string memory) { - return _name; - } - function symbol() public view override returns (string memory) { - return _symbol; - } - function decimals() public view override returns (uint8) { - return _decimals; - } - function totalSupply() public view override returns (uint256) { - return totalVdodoAmount; + name = _name; + symbol = _symbol; + decimals = 18; + _DODO_APPROVE_PROXY_ = _dodoApproveProxy; + _DOOD_GOV_ = _dodoGov; + _DODO_LOCKED_HELPER_ = _dodoLockedHelper; + _DODO_TOKEN_ = _dodoToken; } // ============ Ownable Functions ============ @@ -120,19 +121,24 @@ contract VDODOToken is IERC20,InitializableOwnable ,ReentrancyGuard{ cantransfer = _allowed; emit SetCantransfer(_allowed); } + + function addOperationAddress(address _operater) public onlyOwner { operater[_operater] = true; emit AddOperation(_operater); } + function removeOperation(address _operater) public onlyOwner { operater[_operater] = false; emit RemoveOperation(_operater); } function changePerReward(uint256 _dodoPerBlock) public onlyOwner { + //TODO: update lastReward? dodoPerBlock = _dodoPerBlock; emit ChangePerReward(dodoPerBlock); } + function updateDodoFeeDestroyRatio(uint256 _dodoFeeDestroyRatio) public onlyOwner { dodoFeeDestroyRatio = _dodoFeeDestroyRatio; emit UpdateDodoFeeDestroyRatio(_dodoFeeDestroyRatio); @@ -147,21 +153,33 @@ contract VDODOToken is IERC20,InitializableOwnable ,ReentrancyGuard{ bytes calldata _data ) external preventReentrant { UserInfo memory user = userInfo[msg.sender]; - require(user.vdodoAmount>_amount,"no enough vdodo"); + require(user.vdodoAmount > _amount, "vDODOToken: no enough vdodo"); if (_data.length > 0) - IGovernance(govAddr).governanceCall(msg.sender, _amount, _data); + IGovernance(_DOOD_GOV_).governanceCall(msg.sender, _amount, _data); uint256 userVdodoAmount = user.vdodoAmount.sub(_amount); + //TODO: 是否减掉总量 + totalSupply = totalSupply.sub(_amount); + //TODO: 欠款为0? _updateUserData(msg.sender,userVdodoAmount,0); - totalVdodoAmount = totalVdodoAmount.sub(_amount); _changeUserParticipateState(msg.sender,true); emit ParticipatoryGov(msg.sender, _amount); } + //TODO round up /down - function deposit(uint256 _amount,address _superiorAddress) public preventReentrant{ - require(_amount>0,"must deposit greater than 0"); - dodo.transferFrom(msg.sender, address(this), _amount); + function deposit(uint256 _amount,address _superiorAddress) public preventReentrant { + require(_amount > 0,"must deposit greater than 0"); + + IDODOApprove(_DODO_APPROVE_PROXY_).claimTokens( + fromToken, + msg.sender, + address(this), + fromTokenAmount + ); + + + IERC20(_DODO_TOKEN_).transferFrom(msg.sender, address(this), _amount); _updateAlpha(); UserInfo memory user = userInfo[msg.sender]; @@ -186,11 +204,11 @@ contract VDODOToken is IERC20,InitializableOwnable ,ReentrancyGuard{ _updateUserData(user.superior,superiorVdodoAmount,superiorOverdraft); - uint256 newTotalOverdraft = totalOverdraft.add(overdraft); - _updateTotalOverdraft(newTotalOverdraft); + uint256 newtotalOverDraft = totalOverDraft.add(overdraft); + _updatetotalOverDraft(newtotalOverDraft); // total sp + x/alpha*1.1 - uint256 newTotalVdodoAmount = totalVdodoAmount.add(_amount.div(alpha).mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); - _updateTotalVdodoAmount(newTotalVdodoAmount); + uint256 newtotalSupply = totalSupply.add(_amount.div(alpha).mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); + _updatetotalSupply(newtotalSupply); emit Deposit(msg.sender,_superiorAddress, _amount); } @@ -217,12 +235,12 @@ contract VDODOToken is IERC20,InitializableOwnable ,ReentrancyGuard{ _updateUserData(user.superior,superiorVdodoAmount,superiorOverdraft); - uint256 newTotalOverdraft = totalOverdraft.sub(overdraft); - _updateTotalOverdraft(newTotalOverdraft); + uint256 newtotalOverDraft = totalOverDraft.sub(overdraft); + _updatetotalOverDraft(newtotalOverDraft); // total sp - (x+x*0.1)//TODO - uint256 newTotalVdodoAmount = totalVdodoAmount.sub(_amount.mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); - _updateTotalVdodoAmount(newTotalVdodoAmount); + uint256 newtotalSupply = totalSupply.sub(_amount.mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); + _updatetotalSupply(newtotalSupply); lastRewardBlock = block.number; @@ -243,7 +261,7 @@ contract VDODOToken is IERC20,InitializableOwnable ,ReentrancyGuard{ if(dodoFeeDestroyRatio>0){ // uint256 dodoFee = dodoAmount.mul(feeRatio).div(_BASE_AMOUNT_); uint256 destroyAmount = dodoFee.mul(dodoFeeDestroyRatio).div(_BASE_AMOUNT_); - transfer(_DESTROY_ADDRESS_, destroyAmount); + transfer(address(0), destroyAmount); distributeAmount = dodoFee.sub(destroyAmount); } @@ -252,7 +270,7 @@ contract VDODOToken is IERC20,InitializableOwnable ,ReentrancyGuard{ // alpha = alpha*X + x * 20% /totalSp uint256 feeAmount = _amount.mul(distributeAmount).div(_BASE_AMOUNT_); - alpha = alpha.mul(_amount).add(feeAmount.div(totalVdodoAmount)); + alpha = alpha.mul(_amount).add(feeAmount.div(totalSupply)); emit Redeem(msg.sender, _amount); } @@ -333,12 +351,14 @@ contract VDODOToken is IERC20,InitializableOwnable ,ReentrancyGuard{ function _updateAlpha() internal { // accuDODO = dodoPerBlock*(block-lastRewardBlock) uint256 accuDODO = dodoPerBlock * (block.number.sub(lastRewardBlock)); - if(totalVdodoAmount > 0){ + if(totalSupply > 0){ // alpha = alpha + accuDODO/totalSp (round down) - alpha = alpha.add(accuDODO.div(totalVdodoAmount)); + alpha = alpha.add(accuDODO.div(totalSupply)); } } + + function _updateUserData(address _who,uint256 _vdodoAmount,uint256 _overdraft) internal { UserInfo storage userWho = userInfo[_who]; if(_vdodoAmount>0){ @@ -364,20 +384,20 @@ contract VDODOToken is IERC20,InitializableOwnable ,ReentrancyGuard{ userWho.hasParticipateGov = _newState; } - function _updateTotalOverdraft(uint256 _overdraft) internal { - totalOverdraft = _overdraft; + function _updatetotalOverDraft(uint256 _overdraft) internal { + totalOverDraft = _overdraft; } - function _updateTotalVdodoAmount(uint256 _newVdodoAmount) internal { - totalVdodoAmount = _newVdodoAmount; + function _updatetotalSupply(uint256 _newVdodoAmount) internal { + totalSupply = _newVdodoAmount; } // ============= Helper and calculation function =============== function checkReward() internal returns(uint256) { - uint256 dodoTotalAmout = IHelper(helperAddr).getDodoAmount(); + uint256 dodoTotalLockedAmout = IDODOLockedHelper(_DODO_LOCKED_HELPER_).getDodoLockedAmount(); // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 ==> y = sqrt(100* (x*x +2x ) / 81)) +15 // y = 5 (x ≤ 1) // y = 15 (x ≥ 10) - uint256 x = dodoTotalAmout.divCeil(totalVdodoAmount); + uint256 x = dodoTotalLockedAmout.divCeil(totalSupply); if(x<=_MIN_X_){ return _MIN_X_Y_; }else if(x>=_MAX_X_){ From eb0b6ca13dc60f040673a1b62f1b5e6b84a3543e Mon Sep 17 00:00:00 2001 From: owen05 Date: Sun, 31 Jan 2021 23:16:27 +0800 Subject: [PATCH 11/42] update vDODO --- contracts/DODOToken/vDODOToken.sol | 304 +++++++++++------------------ 1 file changed, 110 insertions(+), 194 deletions(-) diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index 6468515..f7a6c9a 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -5,15 +5,15 @@ */ pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; import {IERC20} from "../intf/IERC20.sol"; -import {Address} from "../lib/Address.sol"; import {SafeMath} from "../lib/SafeMath.sol"; import {DecimalMath} from "../lib/DecimalMath.sol"; import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; import {SafeERC20} from "../lib/SafeERC20.sol"; import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; - +import {IDODOApproveProxy} from "../SmartRoute/DODOApproveProxy.sol"; interface IGovernance { function governanceCall(address account, uint256 amount,bytes calldata data) external returns (bool); @@ -25,7 +25,7 @@ interface IDODOLockedHelper { } -contract vDODOToken is IERC20, InitializableOwnable ,ReentrancyGuard{ +contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ using SafeMath for uint256; using SafeERC20 for IERC20; @@ -46,21 +46,16 @@ contract vDODOToken is IERC20, InitializableOwnable ,ReentrancyGuard{ bool cantransfer; uint256 public dodoPerBlock; - uint256 constant public _MAG_SP_AMOUNT_ = 10; - //TODO: 可去掉 - uint256 constant public _MAG_TOTALSP_AMOUNT_ = 110; - uint256 constant public _BASE_AMOUNT_ = 100; + uint256 constant public _MAG_SP_AMOUNT_ = 10; // 0.1 - uint256 constant public _MIN_X_ = 1; - uint256 constant public _MIN_X_Y_ = 5; - uint256 constant public _MAX_X_ = 10; - uint256 constant public _MAX_X_Y_ = 15; + uint256 constant public _MIN_X_ = 10**18; + uint256 constant public _MAX_X_ = 10 * 10**18; + uint256 constant public _MIN_Y_ = 5 * 10**18; + uint256 constant public _MAX_Y_ = 15 * 10**18; uint256 public alpha = 100; - uint256 public totalOverDraft; uint256 public lastRewardBlock; uint256 public dodoFeeDestroyRatio; - mapping(address => mapping(address => uint256)) internal _ALLOWED_; mapping(address => bool) public operater; mapping(address => UserInfo) public userInfo; @@ -68,14 +63,13 @@ contract vDODOToken is IERC20, InitializableOwnable ,ReentrancyGuard{ address superior; uint256 vdodoAmount; uint256 overdraft; - uint256 totalRedeem; bool hasParticipateGov; //是否正在参与治理,是的话就不可以提币 } // ============ Events ============ event ParticipatoryGov(address user, uint256 amount); event Deposit(address user,address superior, uint256 amount); - event Redeem(address user, uint256 amount); + event Withdraw(address user, uint256 amount); event SetCantransfer(bool allowed); event RemoveOperation(address operater); event AddOperation(address operater); @@ -93,11 +87,6 @@ contract vDODOToken is IERC20, InitializableOwnable ,ReentrancyGuard{ _; } - //TODO:是否可以不写? - receive() external payable { - revert(); - } - constructor( address _dodoGov, address _dodoToken, @@ -113,16 +102,16 @@ contract vDODOToken is IERC20, InitializableOwnable ,ReentrancyGuard{ _DOOD_GOV_ = _dodoGov; _DODO_LOCKED_HELPER_ = _dodoLockedHelper; _DODO_TOKEN_ = _dodoToken; + lastRewardBlock = block.number; } - // ============ Ownable Functions ============ + // ============ Ownable Functions ============` function setCantransfer(bool _allowed) public onlyOwner { cantransfer = _allowed; emit SetCantransfer(_allowed); } - function addOperationAddress(address _operater) public onlyOwner { operater[_operater] = true; emit AddOperation(_operater); @@ -134,7 +123,7 @@ contract vDODOToken is IERC20, InitializableOwnable ,ReentrancyGuard{ } function changePerReward(uint256 _dodoPerBlock) public onlyOwner { - //TODO: update lastReward? + _updateAlpha(); dodoPerBlock = _dodoPerBlock; emit ChangePerReward(dodoPerBlock); } @@ -144,156 +133,125 @@ contract vDODOToken is IERC20, InitializableOwnable ,ReentrancyGuard{ emit UpdateDodoFeeDestroyRatio(_dodoFeeDestroyRatio); } - // ============ Functions ============ - - //TODO 投票与代理是否分开 function participatoryGov( uint256 _amount, bytes calldata _data ) external preventReentrant { - UserInfo memory user = userInfo[msg.sender]; + UserInfo storage user = userInfo[msg.sender]; require(user.vdodoAmount > _amount, "vDODOToken: no enough vdodo"); if (_data.length > 0) IGovernance(_DOOD_GOV_).governanceCall(msg.sender, _amount, _data); - uint256 userVdodoAmount = user.vdodoAmount.sub(_amount); - //TODO: 是否减掉总量 + user.vdodoAmount = user.vdodoAmount.sub(_amount); + user.hasParticipateGov = true; + //TODO: 是否减掉总量? totalSupply = totalSupply.sub(_amount); - //TODO: 欠款为0? - _updateUserData(msg.sender,userVdodoAmount,0); - _changeUserParticipateState(msg.sender,true); + emit ParticipatoryGov(msg.sender, _amount); } - //TODO round up /down - function deposit(uint256 _amount,address _superiorAddress) public preventReentrant { - require(_amount > 0,"must deposit greater than 0"); - - IDODOApprove(_DODO_APPROVE_PROXY_).claimTokens( - fromToken, + function deposit(uint256 _dodoAmount,address _superiorAddress) public preventReentrant { + require(_dodoAmount > 0, "vDODOToken: must deposit greater than 0"); + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( + _DODO_TOKEN_, msg.sender, address(this), - fromTokenAmount + _dodoAmount ); - - IERC20(_DODO_TOKEN_).transferFrom(msg.sender, address(this), _amount); _updateAlpha(); - UserInfo memory user = userInfo[msg.sender]; - // 自己的sp + x/alpha - uint256 newVdodoAmount = _amount.div(alpha); - uint256 fromVdodoAmount = user.vdodoAmount.add(newVdodoAmount); - _updateUserData(msg.sender,fromVdodoAmount,0); + UserInfo storage user = userInfo[msg.sender]; + user.vdodoAmount = user.vdodoAmount.add(_dodoAmount.div(alpha)); + + if(user.superior == address(0) && _superiorAddress != address(0) && _superiorAddress != msg.sender){ + user.superior = _superiorAddress; + } + uint256 _dodoAmountDivAlpha = DecimalMath.divFloor(_dodoAmount, alpha); + + if(user.superior != address(0)){ + UserInfo storage superiorUser = userInfo[user.superior]; + + superiorUser.vdodoAmount = superiorUser.vdodoAmount.add(_dodoAmountDivAlpha.mul(_MAG_SP_AMOUNT_).div(100)); - if(user.superior==address(0x0) && _superiorAddress != address(0x0)){ - _updateSuperiorAddress(msg.sender,_superiorAddress); + superiorUser.overdraft = superiorUser.overdraft.add(_dodoAmount.mul(_MAG_SP_AMOUNT_).div(100)); + + totalSupply = totalSupply.add(_dodoAmountDivAlpha.mul(_MAG_SP_AMOUNT_ + 100).div(100)); + }else { + totalSupply = totalSupply.add(_dodoAmountDivAlpha); } - UserInfo memory superiorUser = userInfo[user.superior]; - // 上级sp +( x/alpha)* 0.1 (round up) - uint256 superiorVdodoAmount = superiorUser.vdodoAmount.add( - _amount.div(alpha) - .mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); - - // 上级DODO欠款 + x*0.1 (round up) - uint256 overdraft = _amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_); - uint256 superiorOverdraft = superiorUser.overdraft.add(overdraft); - - _updateUserData(user.superior,superiorVdodoAmount,superiorOverdraft); - - uint256 newtotalOverDraft = totalOverDraft.add(overdraft); - _updatetotalOverDraft(newtotalOverDraft); - // total sp + x/alpha*1.1 - uint256 newtotalSupply = totalSupply.add(_amount.div(alpha).mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); - _updatetotalSupply(newtotalSupply); - emit Deposit(msg.sender,_superiorAddress, _amount); + emit Deposit(msg.sender, _superiorAddress, _dodoAmount); } - //TODO round up /down - function redeem(uint256 _amount) public preventReentrant{ - UserInfo memory user = userInfo[msg.sender]; - require(user.vdodoAmount>=_amount,"no enough vdodo token"); - require(!user.hasParticipateGov,"hasParticipateGov"); + + function withdraw(uint256 _vDodoAmount) public preventReentrant { + UserInfo storage user = userInfo[msg.sender]; + uint256 userAmount = user.vdodoAmount; + require(userAmount >= _vDodoAmount, "vDODOToken: no enough vdodo token"); + require(!user.hasParticipateGov, "vDODOToken: hasParticipateGov"); + _updateAlpha(); - // 自己的sp - x + user.vdodoAmount = userAmount.sub(_vDodoAmount); - uint256 userVdodoAmount = user.vdodoAmount.sub(_amount); - _updateUserData(msg.sender,userVdodoAmount,0); + if(user.superior != address(0)) { + UserInfo storage superiorUser = userInfo[user.superior]; + superiorUser.vdodoAmount = superiorUser.vdodoAmount.sub(_vDodoAmount.mul(_MAG_SP_AMOUNT_).div(100)); - UserInfo memory superiorUser = userInfo[user.superior]; - // 上级sp - (x)*0.1(round down) - uint256 superiorVdodoAmount = superiorUser.vdodoAmount.sub( - _amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); + uint256 _overdraft = _vDodoAmount.mul(alpha).mul(_MAG_SP_AMOUNT_).div(100); + superiorUser.overdraft = superiorUser.overdraft.sub(_overdraft); - // 上级DODO欠款 - x*alpha*0.1 (round down) - uint256 overdraft = _amount.mul(alpha).mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_); - uint256 superiorOverdraft= superiorUser.overdraft.sub(overdraft); - _updateUserData(user.superior,superiorVdodoAmount,superiorOverdraft); + totalSupply = totalSupply.sub(_vDodoAmount.mul(_MAG_SP_AMOUNT_ + 100).div(100)); + } else { + totalSupply = totalSupply.sub(_vDodoAmount); + } + + uint256 feeRatio = _checkReward(); + uint256 withdrawDodoAmount = alpha.mul(_vDodoAmount); - uint256 newtotalOverDraft = totalOverDraft.sub(overdraft); - _updatetotalOverDraft(newtotalOverDraft); + uint256 withdrawFeeAmount = DecimalMath.mulCeil(withdrawDodoAmount,feeRatio).div(100); + uint256 dodoReceive = withdrawDodoAmount.sub(withdrawFeeAmount); - // total sp - (x+x*0.1)//TODO - uint256 newtotalSupply = totalSupply.sub(_amount.mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_)); - _updatetotalSupply(newtotalSupply); + IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive); - lastRewardBlock = block.number; - - uint256 feeRatio = checkReward(); - // alpha* x * 80% transfer to user - uint256 dodoAmount = alpha.mul(_amount); - - uint256 dodoFee = dodoAmount.mul(feeRatio).div(_BASE_AMOUNT_); - uint256 dodoReceive = dodoAmount.sub(dodoFee); - - dodo.safeTransferFrom(address(this), msg.sender, dodoReceive); - uint256 newRedeem = user.totalRedeem.add(dodoReceive); - _updateUserRedeem(msg.sender,newRedeem); - - // 3. 这部分税会继续拆成两部分,第一部分销毁,第二部分分给所有vDODO持有人 - uint256 distributeAmount = dodoFee; - //是否需要销毁 - if(dodoFeeDestroyRatio>0){ - // uint256 dodoFee = dodoAmount.mul(feeRatio).div(_BASE_AMOUNT_); - uint256 destroyAmount = dodoFee.mul(dodoFeeDestroyRatio).div(_BASE_AMOUNT_); - transfer(address(0), destroyAmount); - distributeAmount = dodoFee.sub(destroyAmount); + if(dodoFeeDestroyRatio > 0){ + uint256 destroyDodoAmount = DecimalMath.mulCeil(withdrawDodoAmount,dodoFeeDestroyRatio).div(100); + _transfer(address(this), address(0), destroyDodoAmount); + withdrawFeeAmount = withdrawFeeAmount.sub(destroyDodoAmount); } - //可以设置 - - // alpha = alpha*X + x * 20% /totalSp - uint256 feeAmount = _amount.mul(distributeAmount).div(_BASE_AMOUNT_); - - alpha = alpha.mul(_amount).add(feeAmount.div(totalSupply)); - emit Redeem(msg.sender, _amount); + alpha = alpha.add(withdrawFeeAmount.div(totalSupply)); + emit Withdraw(msg.sender, _vDodoAmount); } - // balanceOf = sp-DODO欠款/alpha - function balanceOf(address _address) public view override returns (uint256 balance) { + // ============ Functions(ERC20) ============ + function balanceOf(address _address) public view returns (uint256 balance) { UserInfo memory user = userInfo[_address]; balance = user.vdodoAmount.sub(user.overdraft.div(alpha)); } - function transfer(address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(msg.sender, recipient, amount); + function transfer(address to, uint256 amount) public returns (bool) { + _transfer(msg.sender, to, amount); return true; } - function approve(address spender, uint256 amount) public virtual override returns (bool) { + function approve(address spender, uint256 amount) public returns (bool) { _approve(msg.sender, spender, amount); return true; } - function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { - _transfer(sender, recipient, amount); - _approve(sender, msg.sender, _ALLOWED_[sender][msg.sender].sub(amount)); + + function transferFrom(address from, address to, uint256 amount) public returns (bool) { + require(amount <= _ALLOWED_[from][msg.sender], "ALLOWANCE_NOT_ENOUGH"); + _transfer(from, to, amount); + _ALLOWED_[from][msg.sender] = _ALLOWED_[from][msg.sender].sub(amount); + emit Transfer(from, to, amount); return true; } + function _approve( address owner, address spender, @@ -303,113 +261,71 @@ contract vDODOToken is IERC20, InitializableOwnable ,ReentrancyGuard{ emit Approval(owner, spender, amount); } - function allowance(address owner, address spender) public view override returns (uint256) { + function allowance(address owner, address spender) public view returns (uint256) { return _ALLOWED_[owner][spender]; } - function _transfer(address from, address to, uint256 _amount) internal onlyOperater virtual { + function _transfer(address from, address to, uint256 _amount) internal onlyOperater { require(from != address(0), " transfer from the zero address"); require(to != address(0), " transfer to the zero address"); - // require(balanceOf(from)≥amount) require(balanceOf(from) >= _amount,"no enough to transfer"); - UserInfo memory user = userInfo[from]; - // sp[from] -= amount - uint256 fromSpAmount = user.vdodoAmount.sub(_amount); - _updateUserData(from,fromSpAmount,0); + + UserInfo storage user = userInfo[from]; + user.vdodoAmount= user.vdodoAmount.sub(_amount); - // sp[上级[from]] -= amount*0.1 (round down) - UserInfo memory fromSuperior = userInfo[user.superior]; - uint256 fromSuperiorSpAmount = fromSuperior.vdodoAmount.sub(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); - _updateUserData(user.superior,fromSuperiorSpAmount,0); + address fromSuperiorAddr = user.superior; - UserInfo memory toUser = userInfo[to]; - // sp[to] += amount - uint256 toSpAmount = toUser.vdodoAmount.add(_amount); - _updateUserData(to,toSpAmount,0); + if(fromSuperiorAddr != address(0)) { + UserInfo storage fromSuperior = userInfo[fromSuperiorAddr]; + fromSuperior.vdodoAmount = fromSuperior.vdodoAmount.sub(_amount.mul(_MAG_SP_AMOUNT_).div(100)); + } - // sp[上级[to]] += amount*0.1 - UserInfo memory toSuperior = userInfo[toUser.superior]; - uint256 toSuperiorSpAmount =toSuperior.vdodoAmount.add(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_)); - _updateUserData(toUser.superior,toSuperiorSpAmount,0); + UserInfo storage toUser = userInfo[to]; + toUser.vdodoAmount = toUser.vdodoAmount.add(_amount); + + address toSuperiorAddr = toUser.superior; + if(toSuperiorAddr != address(0)) { + UserInfo storage toSuperior = userInfo[toSuperiorAddr]; + toUser.vdodoAmount =toSuperior.vdodoAmount.add(_amount.mul(_MAG_SP_AMOUNT_).div(100)); + } emit Transfer(from, to, _amount); } - // 可提取DODO数额 = sp*alpha - DODO欠款 + // ============ View Functions ============ function canWithDraw(address _address) public view returns (uint256 withDrawAmount) { UserInfo memory user = userInfo[_address]; withDrawAmount = user.vdodoAmount.mul(alpha).sub(user.overdraft); } - function checkUserInfo(address _userAddress) public view returns(address,uint256,uint256,uint256,bool) { - UserInfo memory user = userInfo[_userAddress]; - return (user.superior, user.vdodoAmount, user.overdraft,user.totalRedeem,user.hasParticipateGov); - - } // ============ internal function ============ function _updateAlpha() internal { - // accuDODO = dodoPerBlock*(block-lastRewardBlock) uint256 accuDODO = dodoPerBlock * (block.number.sub(lastRewardBlock)); if(totalSupply > 0){ - // alpha = alpha + accuDODO/totalSp (round down) alpha = alpha.add(accuDODO.div(totalSupply)); } + lastRewardBlock = block.number; } - - - function _updateUserData(address _who,uint256 _vdodoAmount,uint256 _overdraft) internal { - UserInfo storage userWho = userInfo[_who]; - if(_vdodoAmount>0){ - userWho.vdodoAmount = _vdodoAmount; - } - if(_overdraft>0){ - userWho.overdraft = _overdraft; - } - } - function _updateUserRedeem(address _who,uint256 _newRedeem) internal { - if(_newRedeem>0){ - UserInfo storage userWho = userInfo[_who]; - userWho.totalRedeem = _newRedeem; - } - } - - function _updateSuperiorAddress(address _who,address _newAddres) internal { - UserInfo storage userWho = userInfo[_who]; - userWho.superior = _newAddres; - } - function _changeUserParticipateState(address _who,bool _newState) internal { - UserInfo storage userWho = userInfo[_who]; - userWho.hasParticipateGov = _newState; - } - - function _updatetotalOverDraft(uint256 _overdraft) internal { - totalOverDraft = _overdraft; - } - - function _updatetotalSupply(uint256 _newVdodoAmount) internal { - totalSupply = _newVdodoAmount; - } // ============= Helper and calculation function =============== - function checkReward() internal returns(uint256) { + function _checkReward() internal returns (uint256) { uint256 dodoTotalLockedAmout = IDODOLockedHelper(_DODO_LOCKED_HELPER_).getDodoLockedAmount(); // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 ==> y = sqrt(100* (x*x +2x ) / 81)) +15 // y = 5 (x ≤ 1) // y = 15 (x ≥ 10) - uint256 x = dodoTotalLockedAmout.divCeil(totalSupply); - if(x<=_MIN_X_){ - return _MIN_X_Y_; - }else if(x>=_MAX_X_){ - return _MAX_X_Y_; + uint256 x = DecimalMath.divCeil(dodoTotalLockedAmout,totalSupply); + if( x <= _MIN_X_){ + return _MIN_Y_; + }else if(x >= _MAX_X_){ + return _MAX_Y_; }else{ - uint256 rewardAmount = x.mul(x).add(x.mul(2)).mul(100).div(81).sqrt().add(15); + uint256 xSubOne = x.sub(10**18); + uint256 rewardAmount = uint256(81 * 10**18).sub(xSubOne.mul(xSubOne)).mul(100).div(81).sqrt().add(15); return rewardAmount; } } } -//官方的收益会定期回购DODO Token并分红。因此要留一个donate接口,方便外部注入资金----> 为什么不可以使用 DODOToken.transfer? - -// TODO DecimalMath calculation \ No newline at end of file +//TODO: donate function? \ No newline at end of file From c7e22612bc3044301baa42d71876704559635fa7 Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 1 Feb 2021 11:17:51 +0800 Subject: [PATCH 12/42] update vDODO --- contracts/DODOToken/vDODOToken.sol | 71 ++++++++++++++++++------------ 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index f7a6c9a..bafa434 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -21,7 +21,7 @@ interface IGovernance { interface IDODOLockedHelper { - function getDodoLockedAmount() external returns (uint256); + function getDodoLockedAmount() external view returns (uint256); } @@ -123,7 +123,7 @@ contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ } function changePerReward(uint256 _dodoPerBlock) public onlyOwner { - _updateAlpha(); + _updateAlpha(getAlpha()); dodoPerBlock = _dodoPerBlock; emit ChangePerReward(dodoPerBlock); } @@ -161,15 +161,15 @@ contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ _dodoAmount ); - _updateAlpha(); + uint tmpAlpha = getAlpha(); UserInfo storage user = userInfo[msg.sender]; - user.vdodoAmount = user.vdodoAmount.add(_dodoAmount.div(alpha)); + user.vdodoAmount = user.vdodoAmount.add(_dodoAmount.div(tmpAlpha)); if(user.superior == address(0) && _superiorAddress != address(0) && _superiorAddress != msg.sender){ user.superior = _superiorAddress; } - uint256 _dodoAmountDivAlpha = DecimalMath.divFloor(_dodoAmount, alpha); + uint256 _dodoAmountDivAlpha = DecimalMath.divFloor(_dodoAmount, tmpAlpha); if(user.superior != address(0)){ UserInfo storage superiorUser = userInfo[user.superior]; @@ -183,6 +183,8 @@ contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ totalSupply = totalSupply.add(_dodoAmountDivAlpha); } + _updateAlpha(tmpAlpha); + emit Deposit(msg.sender, _superiorAddress, _dodoAmount); } @@ -193,38 +195,30 @@ contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ require(userAmount >= _vDodoAmount, "vDODOToken: no enough vdodo token"); require(!user.hasParticipateGov, "vDODOToken: hasParticipateGov"); - _updateAlpha(); - + uint256 tmpAlpha = getAlpha(); + user.vdodoAmount = userAmount.sub(_vDodoAmount); if(user.superior != address(0)) { UserInfo storage superiorUser = userInfo[user.superior]; superiorUser.vdodoAmount = superiorUser.vdodoAmount.sub(_vDodoAmount.mul(_MAG_SP_AMOUNT_).div(100)); - uint256 _overdraft = _vDodoAmount.mul(alpha).mul(_MAG_SP_AMOUNT_).div(100); + uint256 _overdraft = _vDodoAmount.mul(tmpAlpha).mul(_MAG_SP_AMOUNT_).div(100); superiorUser.overdraft = superiorUser.overdraft.sub(_overdraft); totalSupply = totalSupply.sub(_vDodoAmount.mul(_MAG_SP_AMOUNT_ + 100).div(100)); } else { totalSupply = totalSupply.sub(_vDodoAmount); } + + (uint256 dodoReceive, uint256 destroyDodoAmount, uint256 withdrawFeeAmount) = getWithdrawAmount(_vDodoAmount); - uint256 feeRatio = _checkReward(); + IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive); + _transfer(address(this), address(0), destroyDodoAmount); - uint256 withdrawDodoAmount = alpha.mul(_vDodoAmount); + tmpAlpha = tmpAlpha.add(withdrawFeeAmount.div(totalSupply)); + _updateAlpha(tmpAlpha); - uint256 withdrawFeeAmount = DecimalMath.mulCeil(withdrawDodoAmount,feeRatio).div(100); - uint256 dodoReceive = withdrawDodoAmount.sub(withdrawFeeAmount); - - IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive); - - if(dodoFeeDestroyRatio > 0){ - uint256 destroyDodoAmount = DecimalMath.mulCeil(withdrawDodoAmount,dodoFeeDestroyRatio).div(100); - _transfer(address(this), address(0), destroyDodoAmount); - withdrawFeeAmount = withdrawFeeAmount.sub(destroyDodoAmount); - } - - alpha = alpha.add(withdrawFeeAmount.div(totalSupply)); emit Withdraw(msg.sender, _vDodoAmount); } @@ -298,18 +292,41 @@ contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ withDrawAmount = user.vdodoAmount.mul(alpha).sub(user.overdraft); } - - // ============ internal function ============ - function _updateAlpha() internal { + function getAlpha() public view returns (uint256) { uint256 accuDODO = dodoPerBlock * (block.number.sub(lastRewardBlock)); if(totalSupply > 0){ - alpha = alpha.add(accuDODO.div(totalSupply)); + return alpha.add(accuDODO.div(totalSupply)); + }else { + return alpha; } + } + + + function getWithdrawAmount(uint256 vDodoAmount) public view returns(uint256 dodoReceive, uint256 destroyDodoAmount, uint256 withdrawFeeAmount) { + uint256 feeRatio = _checkReward(); + uint256 tmpAlpha = getAlpha(); + uint256 withdrawDodoAmount = tmpAlpha.mul(vDodoAmount); + + withdrawFeeAmount = DecimalMath.mulCeil(withdrawDodoAmount,feeRatio).div(100); + dodoReceive = withdrawDodoAmount.sub(withdrawFeeAmount); + + if(dodoFeeDestroyRatio > 0){ + destroyDodoAmount = DecimalMath.mulCeil(withdrawDodoAmount,dodoFeeDestroyRatio).div(100); + withdrawFeeAmount = withdrawFeeAmount.sub(destroyDodoAmount); + }else { + destroyDodoAmount = 0; + } + } + + + // ============ internal function ============ + function _updateAlpha(uint256 newAlpha) internal { + alpha = newAlpha; lastRewardBlock = block.number; } // ============= Helper and calculation function =============== - function _checkReward() internal returns (uint256) { + function _checkReward() internal view returns (uint256) { uint256 dodoTotalLockedAmout = IDODOLockedHelper(_DODO_LOCKED_HELPER_).getDodoLockedAmount(); // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 ==> y = sqrt(100* (x*x +2x ) / 81)) +15 // y = 5 (x ≤ 1) From 0f2aa6c36d95d4035663b1592fe5addca9e49e88 Mon Sep 17 00:00:00 2001 From: mingda Date: Mon, 1 Feb 2021 11:53:22 +0800 Subject: [PATCH 13/42] update vDODO --- contracts/DODOToken/vDODOToken.sol | 333 ++++++++++++++--------------- 1 file changed, 165 insertions(+), 168 deletions(-) diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index f7a6c9a..ea84a7d 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -16,18 +16,18 @@ import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; import {IDODOApproveProxy} from "../SmartRoute/DODOApproveProxy.sol"; interface IGovernance { - function governanceCall(address account, uint256 amount,bytes calldata data) external returns (bool); + function getLockedvDODO(address account) external returns (uint256); } +interface IDODOCirculationHelper { + // vDODO 锁仓不算流通 + function getCirculation() external returns (uint256); -interface IDODOLockedHelper { - function getDodoLockedAmount() external returns (uint256); + function getVDODOWithdrawFeeRatio() external returns (uint256); } - -contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ +contract vDODOToken is InitializableOwnable, ReentrancyGuard { using SafeMath for uint256; - using SafeERC20 for IERC20; // ============ Storage(ERC20) ============ @@ -37,70 +37,72 @@ contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ uint256 public totalSupply; mapping(address => mapping(address => uint256)) internal _ALLOWED_; - // ============ Storage ============ - address immutable _DODO_LOCKED_HELPER_; + address immutable _DODO_TOKEN_; address immutable _DODO_APPROVE_PROXY_; address public _DOOD_GOV_; - bool cantransfer; - uint256 public dodoPerBlock; + address public _DODO_CIRCULATION_HELPER_; - uint256 constant public _MAG_SP_AMOUNT_ = 10; // 0.1 - - uint256 constant public _MIN_X_ = 10**18; - uint256 constant public _MAX_X_ = 10 * 10**18; - uint256 constant public _MIN_Y_ = 5 * 10**18; - uint256 constant public _MAX_Y_ = 15 * 10**18; - - uint256 public alpha = 100; - uint256 public lastRewardBlock; + bool public _CAN_TRANSFER_; + + // staking reward parameters + uint256 public dodoPerBlock; + uint256 public constant _SUPERIOR_RATIO_ = 10**17; // 0.1 uint256 public dodoFeeDestroyRatio; - mapping(address => bool) public operater; + + // accounting + uint256 public alpha = 100 * 10**18; // 100 + uint256 public lastRewardBlock; mapping(address => UserInfo) public userInfo; - + struct UserInfo { + uint256 VDODOAmount; + uint256 credit; address superior; - uint256 vdodoAmount; - uint256 overdraft; - bool hasParticipateGov; //是否正在参与治理,是的话就不可以提币 + uint256 superiorVDODO; } // ============ Events ============ - event ParticipatoryGov(address user, uint256 amount); - event Deposit(address user,address superior, uint256 amount); + + event Deposit(address user, address superior, uint256 amount); event Withdraw(address user, uint256 amount); event SetCantransfer(bool allowed); - event RemoveOperation(address operater); - event AddOperation(address operater); + event ChangePerReward(uint256 dodoPerBlock); event UpdateDodoFeeDestroyRatio(uint256 dodoFeeDestroyRatio); event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); - // ============ Modifiers ============ - //TODO: 是否需要operator的白名单设计? - modifier onlyOperater() { - require(cantransfer || operater[msg.sender] , "vDODOToken: not allowed transfer"); + + modifier canTransfer() { + require(_CAN_TRANSFER_, "vDODOToken: not allowed transfer"); _; } + modifier balanceEnough(address account, uint256 amount) { + require(availableBalanceOf(account) >= amount, "vDODOToken: available amount not enough"); + _; + } + + // ============ Constructor ============ + constructor( address _dodoGov, address _dodoToken, - address _dodoLockedHelper, + address _dodoCirculationHelper, address _dodoApproveProxy, - string memory _name, - string memory _symbol) - public { + string memory _name, + string memory _symbol + ) public { name = _name; symbol = _symbol; decimals = 18; _DODO_APPROVE_PROXY_ = _dodoApproveProxy; _DOOD_GOV_ = _dodoGov; - _DODO_LOCKED_HELPER_ = _dodoLockedHelper; + _DODO_CIRCULATION_HELPER_ = _dodoCirculationHelper; _DODO_TOKEN_ = _dodoToken; lastRewardBlock = block.number; } @@ -108,20 +110,10 @@ contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ // ============ Ownable Functions ============` function setCantransfer(bool _allowed) public onlyOwner { - cantransfer = _allowed; + _CAN_TRANSFER_ = _allowed; emit SetCantransfer(_allowed); } - function addOperationAddress(address _operater) public onlyOwner { - operater[_operater] = true; - emit AddOperation(_operater); - } - - function removeOperation(address _operater) public onlyOwner { - operater[_operater] = false; - emit RemoveOperation(_operater); - } - function changePerReward(uint256 _dodoPerBlock) public onlyOwner { _updateAlpha(); dodoPerBlock = _dodoPerBlock; @@ -132,27 +124,14 @@ contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ dodoFeeDestroyRatio = _dodoFeeDestroyRatio; emit UpdateDodoFeeDestroyRatio(_dodoFeeDestroyRatio); } - - // ============ Functions ============ - function participatoryGov( - uint256 _amount, - bytes calldata _data - ) external preventReentrant { - UserInfo storage user = userInfo[msg.sender]; - require(user.vdodoAmount > _amount, "vDODOToken: no enough vdodo"); - if (_data.length > 0) - IGovernance(_DOOD_GOV_).governanceCall(msg.sender, _amount, _data); - user.vdodoAmount = user.vdodoAmount.sub(_amount); - user.hasParticipateGov = true; - //TODO: 是否减掉总量? - totalSupply = totalSupply.sub(_amount); - - emit ParticipatoryGov(msg.sender, _amount); + function updateDODOCirculationHelper(address _helper) public onlyOwner { + _DODO_CIRCULATION_HELPER_ = _helper; } + // ============ Functions ============ - function deposit(uint256 _dodoAmount,address _superiorAddress) public preventReentrant { + function mint(uint256 _dodoAmount, address _superiorAddress) public preventReentrant { require(_dodoAmount > 0, "vDODOToken: must deposit greater than 0"); IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( _DODO_TOKEN_, @@ -162,76 +141,70 @@ contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ ); _updateAlpha(); + uint256 newVdodoAmount = DecimalMath.divFloor(_dodoAmount, alpha); UserInfo storage user = userInfo[msg.sender]; - user.vdodoAmount = user.vdodoAmount.add(_dodoAmount.div(alpha)); - - if(user.superior == address(0) && _superiorAddress != address(0) && _superiorAddress != msg.sender){ + _mint(user, newVdodoAmount); + + uint256 superiorVDODO; + if (user.superior == address(0) && _superiorAddress != address(0)) { + require(_superiorAddress != msg.sender, "COULD NOT SET SELF AS SUPERIOR"); + superiorVDODO = DecimalMath.divFloor(user.VDODOAmount, _SUPERIOR_RATIO_); user.superior = _superiorAddress; + } else if (user.superior != address(0)) { + superiorVDODO = DecimalMath.divFloor(newVdodoAmount, _SUPERIOR_RATIO_); } - uint256 _dodoAmountDivAlpha = DecimalMath.divFloor(_dodoAmount, alpha); - - if(user.superior != address(0)){ - UserInfo storage superiorUser = userInfo[user.superior]; - - superiorUser.vdodoAmount = superiorUser.vdodoAmount.add(_dodoAmountDivAlpha.mul(_MAG_SP_AMOUNT_).div(100)); - superiorUser.overdraft = superiorUser.overdraft.add(_dodoAmount.mul(_MAG_SP_AMOUNT_).div(100)); - - totalSupply = totalSupply.add(_dodoAmountDivAlpha.mul(_MAG_SP_AMOUNT_ + 100).div(100)); - }else { - totalSupply = totalSupply.add(_dodoAmountDivAlpha); - } + _mintToSuperior(user, superiorVDODO); emit Deposit(msg.sender, _superiorAddress, _dodoAmount); } - - function withdraw(uint256 _vDodoAmount) public preventReentrant { - UserInfo storage user = userInfo[msg.sender]; - uint256 userAmount = user.vdodoAmount; - require(userAmount >= _vDodoAmount, "vDODOToken: no enough vdodo token"); - require(!user.hasParticipateGov, "vDODOToken: hasParticipateGov"); - + function redeem(uint256 _vDodoAmount) + public + preventReentrant + balanceEnough(msg.sender, _vDodoAmount) + { _updateAlpha(); - user.vdodoAmount = userAmount.sub(_vDodoAmount); + UserInfo storage user = userInfo[msg.sender]; + _redeem(user, _vDodoAmount); - if(user.superior != address(0)) { - UserInfo storage superiorUser = userInfo[user.superior]; - superiorUser.vdodoAmount = superiorUser.vdodoAmount.sub(_vDodoAmount.mul(_MAG_SP_AMOUNT_).div(100)); - - uint256 _overdraft = _vDodoAmount.mul(alpha).mul(_MAG_SP_AMOUNT_).div(100); - superiorUser.overdraft = superiorUser.overdraft.sub(_overdraft); - - totalSupply = totalSupply.sub(_vDodoAmount.mul(_MAG_SP_AMOUNT_ + 100).div(100)); - } else { - totalSupply = totalSupply.sub(_vDodoAmount); + if (user.superior != address(0)) { + uint256 superiorRedeemVDODO = DecimalMath.divFloor(_vDodoAmount, _SUPERIOR_RATIO_); + _redeemFromSuperior(user, superiorRedeemVDODO); } - - uint256 feeRatio = _checkReward(); - uint256 withdrawDodoAmount = alpha.mul(_vDodoAmount); + uint256 feeRatio = + IDODOCirculationHelper(_DODO_CIRCULATION_HELPER_).getVDODOWithdrawFeeRatio(); + uint256 withdrawDodoAmount = DecimalMath.mulFloor(_vDodoAmount, alpha); - uint256 withdrawFeeAmount = DecimalMath.mulCeil(withdrawDodoAmount,feeRatio).div(100); + uint256 withdrawFeeAmount = DecimalMath.mulCeil(withdrawDodoAmount, feeRatio); uint256 dodoReceive = withdrawDodoAmount.sub(withdrawFeeAmount); IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive); - if(dodoFeeDestroyRatio > 0){ - uint256 destroyDodoAmount = DecimalMath.mulCeil(withdrawDodoAmount,dodoFeeDestroyRatio).div(100); + if (dodoFeeDestroyRatio > 0) { + uint256 destroyDodoAmount = + DecimalMath.mulCeil(withdrawDodoAmount, dodoFeeDestroyRatio); _transfer(address(this), address(0), destroyDodoAmount); withdrawFeeAmount = withdrawFeeAmount.sub(destroyDodoAmount); } - alpha = alpha.add(withdrawFeeAmount.div(totalSupply)); + alpha = alpha.add(DecimalMath.divFloor(withdrawFeeAmount, totalSupply)); emit Withdraw(msg.sender, _vDodoAmount); } // ============ Functions(ERC20) ============ - function balanceOf(address _address) public view returns (uint256 balance) { - UserInfo memory user = userInfo[_address]; - balance = user.vdodoAmount.sub(user.overdraft.div(alpha)); + + function balanceOf(address account) public view returns (uint256 balance) { + UserInfo memory user = userInfo[account]; + balance = user.VDODOAmount.sub(DecimalMath.divFloor(user.credit, alpha)); + } + + function availableBalanceOf(address account) public returns (uint256 balance) { + uint256 lockedBalance = IGovernance(_DOOD_GOV_).getLockedvDODO(account); + balance = balanceOf(account).sub(lockedBalance); } function transfer(address to, uint256 amount) public returns (bool) { @@ -240,11 +213,16 @@ contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ } function approve(address spender, uint256 amount) public returns (bool) { - _approve(msg.sender, spender, amount); + _ALLOWED_[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); return true; } - function transferFrom(address from, address to, uint256 amount) public returns (bool) { + function transferFrom( + address from, + address to, + uint256 amount + ) public returns (bool) { require(amount <= _ALLOWED_[from][msg.sender], "ALLOWANCE_NOT_ENOUGH"); _transfer(from, to, amount); _ALLOWED_[from][msg.sender] = _ALLOWED_[from][msg.sender].sub(amount); @@ -252,80 +230,99 @@ contract vDODOToken is InitializableOwnable ,ReentrancyGuard{ return true; } - function _approve( - address owner, - address spender, - uint256 amount - ) private onlyOperater { - _ALLOWED_[owner][spender] = amount; - emit Approval(owner, spender, amount); - } - function allowance(address owner, address spender) public view returns (uint256) { return _ALLOWED_[owner][spender]; } - function _transfer(address from, address to, uint256 _amount) internal onlyOperater { - require(from != address(0), " transfer from the zero address"); - require(to != address(0), " transfer to the zero address"); - require(balanceOf(from) >= _amount,"no enough to transfer"); - - UserInfo storage user = userInfo[from]; - user.vdodoAmount= user.vdodoAmount.sub(_amount); - - address fromSuperiorAddr = user.superior; - - if(fromSuperiorAddr != address(0)) { - UserInfo storage fromSuperior = userInfo[fromSuperiorAddr]; - fromSuperior.vdodoAmount = fromSuperior.vdodoAmount.sub(_amount.mul(_MAG_SP_AMOUNT_).div(100)); - } - - UserInfo storage toUser = userInfo[to]; - toUser.vdodoAmount = toUser.vdodoAmount.add(_amount); - - address toSuperiorAddr = toUser.superior; - if(toSuperiorAddr != address(0)) { - UserInfo storage toSuperior = userInfo[toSuperiorAddr]; - toUser.vdodoAmount =toSuperior.vdodoAmount.add(_amount.mul(_MAG_SP_AMOUNT_).div(100)); - } - - emit Transfer(from, to, _amount); - } - // ============ View Functions ============ + function canWithDraw(address _address) public view returns (uint256 withDrawAmount) { UserInfo memory user = userInfo[_address]; - withDrawAmount = user.vdodoAmount.mul(alpha).sub(user.overdraft); + withDrawAmount = user.VDODOAmount.mul(alpha).sub(user.credit); } + // ============ internal function ============ - // ============ internal function ============ function _updateAlpha() internal { uint256 accuDODO = dodoPerBlock * (block.number.sub(lastRewardBlock)); - if(totalSupply > 0){ - alpha = alpha.add(accuDODO.div(totalSupply)); + if (totalSupply > 0) { + alpha = alpha.add(DecimalMath.divFloor(accuDODO, totalSupply)); } lastRewardBlock = block.number; } - // ============= Helper and calculation function =============== - function _checkReward() internal returns (uint256) { - uint256 dodoTotalLockedAmout = IDODOLockedHelper(_DODO_LOCKED_HELPER_).getDodoLockedAmount(); - // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 ==> y = sqrt(100* (x*x +2x ) / 81)) +15 - // y = 5 (x ≤ 1) - // y = 15 (x ≥ 10) - uint256 x = DecimalMath.divCeil(dodoTotalLockedAmout,totalSupply); - if( x <= _MIN_X_){ - return _MIN_Y_; - }else if(x >= _MAX_X_){ - return _MAX_Y_; - }else{ - uint256 xSubOne = x.sub(10**18); - uint256 rewardAmount = uint256(81 * 10**18).sub(xSubOne.mul(xSubOne)).mul(100).div(81).sqrt().add(15); - return rewardAmount; + function _mint(UserInfo storage to, uint256 amount) internal { + to.VDODOAmount = to.VDODOAmount.add(amount); + totalSupply = totalSupply.add(amount); + } + + function _mintToSuperior(UserInfo storage user, uint256 vdodoAmount) internal { + if (vdodoAmount > 0) { + user.superiorVDODO = user.superiorVDODO.add(vdodoAmount); + UserInfo storage superiorUser = userInfo[user.superior]; + _mint(superiorUser, vdodoAmount); + superiorUser.credit = superiorUser.credit.add(DecimalMath.mulFloor(vdodoAmount, alpha)); } } + + function _redeem(UserInfo storage from, uint256 amount) internal { + from.VDODOAmount = from.VDODOAmount.sub(amount); + totalSupply = totalSupply.sub(amount); + } + + function _redeemFromSuperior(UserInfo storage user, uint256 vdodoAmount) internal { + if (vdodoAmount > 0) { + // 最多撤销当时给superior的 + vdodoAmount = user.superiorVDODO <= vdodoAmount ? user.superiorVDODO : vdodoAmount; + user.superiorVDODO = user.superiorVDODO.sub(vdodoAmount); + + // 最多撤销superior的全部credit + UserInfo storage superiorUser = userInfo[user.superior]; + uint256 creditVDODO = DecimalMath.divFloor(superiorUser.credit, alpha); + + if (vdodoAmount >= creditVDODO) { + superiorUser.credit = 0; + _redeem(superiorUser, creditVDODO); + } else { + superiorUser.credit = superiorUser.credit.sub( + DecimalMath.mulFloor(vdodoAmount, alpha) + ); + _redeem(superiorUser, vdodoAmount); + } + } + } + + function _transfer( + address from, + address to, + uint256 _amount + ) internal balanceEnough(msg.sender, _amount) { + require(from != address(0), "transfer from the zero address"); + require(to != address(0), "transfer to the zero address"); + + UserInfo storage fromUser = userInfo[from]; + fromUser.VDODOAmount = fromUser.VDODOAmount.sub(_amount); + + UserInfo storage toUser = userInfo[to]; + toUser.VDODOAmount = toUser.VDODOAmount.add(_amount); + + uint256 superiorRedeemVDODO = DecimalMath.divFloor(_amount, _SUPERIOR_RATIO_); + + address fromSuperiorAddr = fromUser.superior; + if (fromSuperiorAddr != address(0)) { + _redeemFromSuperior(fromUser, superiorRedeemVDODO); + } + + address toSuperiorAddr = toUser.superior; + if (toSuperiorAddr != address(0)) { + _mintToSuperior(toUser, superiorRedeemVDODO); + } + + emit Transfer(from, to, _amount); + } + + function donate(uint256 amount) public { + IERC20(_DODO_TOKEN_).transferFrom(msg.sender, address(this), amount); + alpha = alpha.add(DecimalMath.divFloor(amount, totalSupply)); + } } - - -//TODO: donate function? \ No newline at end of file From e792d157368f8ac0ea461860b0b95cb515b3cf36 Mon Sep 17 00:00:00 2001 From: mingda Date: Mon, 1 Feb 2021 11:58:45 +0800 Subject: [PATCH 14/42] vDODO --- contracts/DODOToken/{vDODOToken_Lei.sol => vDODOToken.sol} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contracts/DODOToken/{vDODOToken_Lei.sol => vDODOToken.sol} (100%) diff --git a/contracts/DODOToken/vDODOToken_Lei.sol b/contracts/DODOToken/vDODOToken.sol similarity index 100% rename from contracts/DODOToken/vDODOToken_Lei.sol rename to contracts/DODOToken/vDODOToken.sol From e12893aa8f1c7317ddf0f3979cd8d58b90759043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Mon, 1 Feb 2021 12:09:33 +0800 Subject: [PATCH 15/42] _superiorAddress != msg.sender --- contracts/DODOToken/vDODOToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index ea84a7d..6c018d2 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -147,7 +147,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { _mint(user, newVdodoAmount); uint256 superiorVDODO; - if (user.superior == address(0) && _superiorAddress != address(0)) { + if (user.superior == address(0) && _superiorAddress != address(0) && _superiorAddress != msg.sender ) { require(_superiorAddress != msg.sender, "COULD NOT SET SELF AS SUPERIOR"); superiorVDODO = DecimalMath.divFloor(user.VDODOAmount, _SUPERIOR_RATIO_); user.superior = _superiorAddress; From 58339f08c170c6e2bc5cbc79f6be4c80096be9b0 Mon Sep 17 00:00:00 2001 From: mingda Date: Mon, 1 Feb 2021 13:31:03 +0800 Subject: [PATCH 16/42] dodo circulation helper --- contracts/DODOToken/DODOCirculationHelper.sol | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/contracts/DODOToken/DODOCirculationHelper.sol b/contracts/DODOToken/DODOCirculationHelper.sol index 05dcd29..0c66fb1 100644 --- a/contracts/DODOToken/DODOCirculationHelper.sol +++ b/contracts/DODOToken/DODOCirculationHelper.sol @@ -1,4 +1,4 @@ - /* +/* Copyright 2020 DODO ZOO. SPDX-License-Identifier: Apache-2.0 @@ -10,10 +10,7 @@ pragma experimental ABIEncoderV2; import {IERC20} from "../intf/IERC20.sol"; import {SafeMath} from "../lib/SafeMath.sol"; import {DecimalMath} from "../lib/DecimalMath.sol"; -import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; -import {SafeERC20} from "../lib/SafeERC20.sol"; -import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; -import {IDODOApproveProxy} from "../SmartRoute/DODOApproveProxy.sol"; +import {Ownable} from "../lib/Ownable.sol"; interface IDODOCirculationHelper { // vDODO 锁仓不算流通 @@ -30,16 +27,30 @@ contract DODOCirculationHelper is Ownable { address immutable _DODO_TOKEN_; address[] _LOCKED_CONTRACT_ADDRESS_; - uint256 public _MIN_PENALTY_RATIO_ = 5 * 10**16; // 5% + uint256 public _MIN_PENALTY_RATIO_ = 5 * 10**16; // 5% uint256 public _MAX_PENALTY_RATIO_ = 15 * 10**16; // 15% - // ============= Helper and calculation function =============== - function getVDODOWithdrawFeeRatio() internal returns (uint256) { - uint256 dodoCirculationAmout = - IDODOCirculationHelper(_DODO_CIRCULATION_HELPER_).getCirculation(); - // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 ==> y = sqrt(100* (x*x +2x ) / 81)) +15 + constructor(address dodoToken) public { + _DODO_TOKEN_ = dodoToken; + } // todo + + function addLockedContractAddress(address lockedContract) external onlyOwner {} // todo + + function removeLockedContractAddress(address lockedContract) external onlyOwner {} // todo + + function getCirculation() public view returns (uint256 circulation) { + circulation = 10**9; + for (uint256 i = 0; i < _LOCKED_CONTRACT_ADDRESS_.length; i++) { + circulation -= IERC20(_DODO_TOKEN_).balanceOf(_LOCKED_CONTRACT_ADDRESS_[i]); + } + } + + function getVDODOWithdrawFeeRatio() external view returns (uint256 ratio) { + uint256 dodoCirculationAmout = getCirculation(); + // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 // y = 5% (x ≤ 1) // y = 15% (x ≥ 10) + // y = 15% - 10% * sqrt(1-[(x-1)/9]^2) uint256 x = DecimalMath.divCeil( dodoCirculationAmout, @@ -51,16 +62,11 @@ contract DODOCirculationHelper is Ownable { } else if (x >= 10**19) { return _MAX_PENALTY_RATIO_; } else { - uint256 xSubOne = x.sub(DecimalMath.ONE); - xSubOne.sub(9) - uint256 rewardAmount = - uint256(81 * 10**18).sub(xSubOne.mul(xSubOne)).mul(100).div(81).sqrt().add(15); - return rewardAmount; + uint256 xTemp = x.sub(DecimalMath.ONE).div(9); + uint256 premium = DecimalMath.ONE2.sub(xTemp.mul(xTemp)).sqrt(); + ratio = + _MAX_PENALTY_RATIO_ - + DecimalMath.mulFloor(_MAX_PENALTY_RATIO_ - _MIN_PENALTY_RATIO_, premium); } } - - - } - - \ No newline at end of file From 368749aa03e2fa6df2e7b17ab56646a07cd839c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Mon, 1 Feb 2021 17:27:44 +0800 Subject: [PATCH 17/42] Add test context --- contracts/DODOToken/DODOCirculationHelper.sol | 3 +- contracts/DODOToken/Governance.sol | 24 ++++ contracts/DODOToken/vDODOToken.sol | 8 +- test/Token/vdodo.test.ts | 75 +++++++++++ test/utils/Contracts.ts | 3 + test/utils/VDODOContext.ts | 120 ++++++++++++++++++ 6 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 contracts/DODOToken/Governance.sol create mode 100644 test/Token/vdodo.test.ts create mode 100644 test/utils/VDODOContext.ts diff --git a/contracts/DODOToken/DODOCirculationHelper.sol b/contracts/DODOToken/DODOCirculationHelper.sol index 0c66fb1..f048713 100644 --- a/contracts/DODOToken/DODOCirculationHelper.sol +++ b/contracts/DODOToken/DODOCirculationHelper.sol @@ -40,6 +40,7 @@ contract DODOCirculationHelper is Ownable { function getCirculation() public view returns (uint256 circulation) { circulation = 10**9; + //TODO circulation 需要乘以decimals吗? for (uint256 i = 0; i < _LOCKED_CONTRACT_ADDRESS_.length; i++) { circulation -= IERC20(_DODO_TOKEN_).balanceOf(_LOCKED_CONTRACT_ADDRESS_[i]); } @@ -54,7 +55,7 @@ contract DODOCirculationHelper is Ownable { uint256 x = DecimalMath.divCeil( dodoCirculationAmout, - IERC20(_DODO_TOKEN_).balanceOf(address(this)) + IERC20(_DODO_TOKEN_).balanceOf(address(this))// TODO 这里应该是vdodo的值吧? ); if (x <= 10**18) { diff --git a/contracts/DODOToken/Governance.sol b/contracts/DODOToken/Governance.sol new file mode 100644 index 0000000..4f2371d --- /dev/null +++ b/contracts/DODOToken/Governance.sol @@ -0,0 +1,24 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; + +contract Governance is InitializableOwnable { + + // ============ Storage ============ + + address immutable _DODO_TOKEN_; + + constructor(address dodoToken) public { + _DODO_TOKEN_ = dodoToken; + } + function getLockedvDODO(address account) external pure returns (uint256 lockedvDODO) { + lockedvDODO = 0;//DOTO,0 for test + } +} diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index 6c018d2..6ae3e84 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -90,18 +90,19 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { // ============ Constructor ============ constructor( - address _dodoGov, + // address _dodoGov, address _dodoToken, address _dodoCirculationHelper, address _dodoApproveProxy, string memory _name, string memory _symbol ) public { + initOwner(msg.sender); name = _name; symbol = _symbol; decimals = 18; _DODO_APPROVE_PROXY_ = _dodoApproveProxy; - _DOOD_GOV_ = _dodoGov; + // _DOOD_GOV_ = _dodoGov; _DODO_CIRCULATION_HELPER_ = _dodoCirculationHelper; _DODO_TOKEN_ = _dodoToken; lastRewardBlock = block.number; @@ -128,6 +129,9 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { function updateDODOCirculationHelper(address _helper) public onlyOwner { _DODO_CIRCULATION_HELPER_ = _helper; } + function updateGovernance(address _governance) public onlyOwner { + _DOOD_GOV_ = _governance; + } // ============ Functions ============ diff --git a/test/Token/vdodo.test.ts b/test/Token/vdodo.test.ts new file mode 100644 index 0000000..9538008 --- /dev/null +++ b/test/Token/vdodo.test.ts @@ -0,0 +1,75 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +// import * as assert from 'assert'; + +import { decimalStr, MAX_UINT256 } from '../utils/Converter'; +import { logGas } from '../utils/Log'; +import { VDODOContext, getVDODOContext } from '../utils/VDODOContext'; +import { assert } from 'chai'; +import BigNumber from 'bignumber.js'; +const truffleAssert = require('truffle-assertions'); + +let account0: string; +let account1: string; +let account2: string; + +async function init(ctx: VDODOContext): Promise { + account0 = ctx.SpareAccounts[0]; + account1 = ctx.SpareAccounts[1]; + account2 = ctx.SpareAccounts[2]; + + await ctx.mintTestToken(account0, decimalStr("1000")); + await ctx.mintTestToken(account1, decimalStr("1000")); + +} + +describe("VDODO", () => { + let snapshotId: string; + let ctx: VDODOContext; + + before(async () => { + ctx = await getVDODOContext(); + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("vdodo", () => { + + it("vdodo init", async () => { + + assert.equal( + await ctx.DODO.methods.balanceOf(account0).call(), + decimalStr("1000") + ); + assert.equal( + await ctx.VDODO.methods.balanceOf(account0).call(), + decimalStr("0") + ); + assert.equal( + await ctx.VDODO.methods.alpha().call(), + ctx.alpha + ); + assert.equal( + await ctx.VDODO.methods.lastRewardBlock().call(), + ctx.lastRewardBlock + ); + assert.equal( + await ctx.VDODO.methods.totalSupply().call(), + decimalStr("0") + ); + + }); + }) +}); diff --git a/test/utils/Contracts.ts b/test/utils/Contracts.ts index 5842d33..7bc7c90 100644 --- a/test/utils/Contracts.ts +++ b/test/utils/Contracts.ts @@ -47,6 +47,9 @@ export const DODO_CALLEE_HELPER_NAME = "DODOCalleeHelper" export const CROWD_POOLING_NAME = "CP" export const CROWD_POOLING_FACTORY = "CrowdPoolingFactory" export const DODO_INCENTIVE = "DODOIncentive" +export const VDODO_NAME = "vDODOToken" +export const DODO_CULATION_HELPER = "DODOCirculationHelper" +export const DODO_GOVERNANCE = "Governance" interface ContractJson { abi: any; diff --git a/test/utils/VDODOContext.ts b/test/utils/VDODOContext.ts new file mode 100644 index 0000000..3827889 --- /dev/null +++ b/test/utils/VDODOContext.ts @@ -0,0 +1,120 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +import BigNumber from 'bignumber.js'; +import Web3 from 'web3'; +import { Contract } from 'web3-eth-contract'; + +import * as contracts from './Contracts'; +import { decimalStr, MAX_UINT256 } from './Converter'; +import { EVM, getDefaultWeb3 } from './EVM'; +import * as log from './Log'; + +BigNumber.config({ + EXPONENTIAL_AT: 1000, + DECIMAL_PLACES: 80, +}); + +export class VDODOContext { + EVM: EVM; + Web3: Web3; + Deployer: string; + Maintainer: string; + SpareAccounts: string[]; + + //token + DODO: Contract; + VDODO: Contract; + + DODOApprove: Contract; + DODOApproveProxy: Contract; + + DODOCirculationHelper: Contract; + Governance: Contract; + + lastRewardBlock:number; + alpha:number; + + + + constructor() { } + + async init() { + this.EVM = new EVM(); + this.Web3 = getDefaultWeb3(); + + this.DODO = await contracts.newContract( + contracts.MINTABLE_ERC20_CONTRACT_NAME, + ["DODO Token", "DODO", 18] + ); + + this.DODOCirculationHelper = await contracts.newContract( + contracts.DODO_CULATION_HELPER, + [ + this.DODO.options.address + ] + ); + this.DODOApprove = await contracts.newContract( + contracts.SMART_APPROVE + ); + + this.DODOApproveProxy = await contracts.newContract( + contracts.SMART_APPROVE_PROXY, + [this.DODOApprove.options.address] + ) + + this.VDODO = await contracts.newContract( + contracts.VDODO_NAME, + [ + this.DODO.options.address, + this.DODOCirculationHelper.options.address, + this.DODOApproveProxy.options.address, + "VDODO Token", "VDODO" + ] + ) + + this.Governance = await contracts.newContract( + contracts.DODO_GOVERNANCE, + [this.VDODO.options.address] + ) + + const allAccounts = await this.Web3.eth.getAccounts(); + this.Deployer = allAccounts[0]; + this.Maintainer = allAccounts[1]; + this.SpareAccounts = allAccounts.slice(2, 10); + + await this.VDODO.methods.updateGovernance( + this.Governance.options.address + ).send(this.sendParam(this.Deployer)) + + this.alpha = await this.VDODO.methods.alpha().call(); + this.lastRewardBlock = await this.VDODO.methods.lastRewardBlock().call(); + + console.log("alpha = "+ this.alpha); + console.log("lastRewardBlock = " + this.lastRewardBlock); + console.log(log.blueText("[Init VDODO context]")); + } + + sendParam(sender, value = "0") { + return { + from: sender, + gas: process.env["COVERAGE"] ? 10000000000 : 7000000, + gasPrice: process.env.GAS_PRICE, + value: decimalStr(value), + }; + } + + async mintTestToken(to: string, amount: string) { + await this.DODO.methods.mint(to, amount).send(this.sendParam(this.Deployer)); + } +} + +export async function getVDODOContext(): Promise { + var context = new VDODOContext(); + await context.init(); + return context; +} From 95717561def0f04bdd439b19157424d4b8d3e9af Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 1 Feb 2021 16:23:45 +0800 Subject: [PATCH 18/42] ing --- contracts/DODOToken/vDODOToken.sol | 47 ++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index 6ae3e84..b15ab36 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -179,23 +179,14 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { _redeemFromSuperior(user, superiorRedeemVDODO); } - uint256 feeRatio = - IDODOCirculationHelper(_DODO_CIRCULATION_HELPER_).getVDODOWithdrawFeeRatio(); - uint256 withdrawDodoAmount = DecimalMath.mulFloor(_vDodoAmount, alpha); + (uint256 dodoReceive, uint256 destroyDodoAmount, uint256 withdrawFeeDodoAmount) = getWithdrawAmount(_vDodoAmount); + + IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive); + _transfer(address(this), address(0), destroyDodoAmount); - uint256 withdrawFeeAmount = DecimalMath.mulCeil(withdrawDodoAmount, feeRatio); - uint256 dodoReceive = withdrawDodoAmount.sub(withdrawFeeAmount); + tmpAlpha = tmpAlpha.add(DecimalMath.divFloor(withdrawFeeDodoAmount, totalSupply)); + _updateAlpha(tmpAlpha); - IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive); - - if (dodoFeeDestroyRatio > 0) { - uint256 destroyDodoAmount = - DecimalMath.mulCeil(withdrawDodoAmount, dodoFeeDestroyRatio); - _transfer(address(this), address(0), destroyDodoAmount); - withdrawFeeAmount = withdrawFeeAmount.sub(destroyDodoAmount); - } - - alpha = alpha.add(DecimalMath.divFloor(withdrawFeeAmount, totalSupply)); emit Withdraw(msg.sender, _vDodoAmount); } @@ -315,7 +306,6 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { address fromSuperiorAddr = fromUser.superior; if (fromSuperiorAddr != address(0)) { _redeemFromSuperior(fromUser, superiorRedeemVDODO); - } address toSuperiorAddr = toUser.superior; if (toSuperiorAddr != address(0)) { @@ -323,6 +313,31 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { } emit Transfer(from, to, _amount); + + } + + function getWithdrawAmount(uint256 vDodoAmount) public view returns(uint256 dodoReceive, uint256 destroyDodoAmount, uint256 withdrawFeeDodoAmount) { + + uint256 feeRatio = + IDODOCirculationHelper(_DODO_CIRCULATION_HELPER_).getVDODOWithdrawFeeRatio(); + + uint256 tmpAlpha = getAlpha(); + uint256 withdrawDodoAmount = DecimalMath.mulFloor(_vDodoAmount, tmpAlpha); + + withdrawFeeDodoAmount = DecimalMath.mulCeil(withdrawDodoAmount, feeRatio); + dodoReceive = withdrawDodoAmount.sub(withdrawFeeDodoAmount); + + if(dodoFeeDestroyRatio > 0){ + destroyDodoAmount = DecimalMath.mulCeil(withdrawDodoAmount,dodoFeeDestroyRatio).div(100); + withdrawFeeDodoAmount = withdrawFeeDodoAmount.sub(destroyDodoAmount); + }else { + destroyDodoAmount = 0; + } + + // ============ internal function ============ + function _updateAlpha(uint256 newAlpha) internal { + alpha = newAlpha; + lastRewardBlock = block.number; } function donate(uint256 amount) public { From 19106c7eaedad7c90bd02793709152ea1680d813 Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 1 Feb 2021 18:35:00 +0800 Subject: [PATCH 19/42] fix --- contracts/DODOToken/vDODOToken.sol | 213 +++++++++++++++-------------- 1 file changed, 110 insertions(+), 103 deletions(-) diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index b15ab36..79c6c69 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -16,14 +16,14 @@ import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; import {IDODOApproveProxy} from "../SmartRoute/DODOApproveProxy.sol"; interface IGovernance { - function getLockedvDODO(address account) external returns (uint256); + function getLockedvDODO(address account) external view returns (uint256); } interface IDODOCirculationHelper { // vDODO 锁仓不算流通 - function getCirculation() external returns (uint256); + function getCirculation() external view returns (uint256); - function getVDODOWithdrawFeeRatio() external returns (uint256); + function getVDODOWithdrawFeeRatio() external view returns (uint256); } contract vDODOToken is InitializableOwnable, ReentrancyGuard { @@ -49,7 +49,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { // staking reward parameters uint256 public dodoPerBlock; uint256 public constant _SUPERIOR_RATIO_ = 10**17; // 0.1 - uint256 public dodoFeeDestroyRatio; + uint256 public dodoFeeBurnRation; // accounting uint256 public alpha = 100 * 10**18; // 100 @@ -70,7 +70,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { event SetCantransfer(bool allowed); event ChangePerReward(uint256 dodoPerBlock); - event UpdateDodoFeeDestroyRatio(uint256 dodoFeeDestroyRatio); + event UpdatedodoFeeBurnRation(uint256 dodoFeeBurnRation); event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); @@ -90,44 +90,44 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { // ============ Constructor ============ constructor( - // address _dodoGov, - address _dodoToken, - address _dodoCirculationHelper, - address _dodoApproveProxy, - string memory _name, - string memory _symbol + address dodoGov, + address dodoToken, + address dodoCirculationHelper, + address dodoApproveProxy, + string memory name, + string memory symbol ) public { initOwner(msg.sender); - name = _name; - symbol = _symbol; + name = name; + symbol = symbol; decimals = 18; - _DODO_APPROVE_PROXY_ = _dodoApproveProxy; - // _DOOD_GOV_ = _dodoGov; - _DODO_CIRCULATION_HELPER_ = _dodoCirculationHelper; - _DODO_TOKEN_ = _dodoToken; + _DODO_APPROVE_PROXY_ = dodoApproveProxy; + _DOOD_GOV_ = dodoGov; + _DODO_CIRCULATION_HELPER_ = dodoCirculationHelper; + _DODO_TOKEN_ = dodoToken; lastRewardBlock = block.number; } // ============ Ownable Functions ============` - function setCantransfer(bool _allowed) public onlyOwner { - _CAN_TRANSFER_ = _allowed; - emit SetCantransfer(_allowed); + function setCantransfer(bool allowed) public onlyOwner { + _CAN_TRANSFER_ = allowed; + emit SetCantransfer(allowed); } - function changePerReward(uint256 _dodoPerBlock) public onlyOwner { - _updateAlpha(); - dodoPerBlock = _dodoPerBlock; + function changePerReward(uint256 dodoPerBlock) public onlyOwner { + _updateAlpha(getLatestAlpha()); + dodoPerBlock = dodoPerBlock; emit ChangePerReward(dodoPerBlock); } - function updateDodoFeeDestroyRatio(uint256 _dodoFeeDestroyRatio) public onlyOwner { - dodoFeeDestroyRatio = _dodoFeeDestroyRatio; - emit UpdateDodoFeeDestroyRatio(_dodoFeeDestroyRatio); + function updatedodoFeeBurnRation(uint256 dodoFeeBurnRation) public onlyOwner { + dodoFeeBurnRation = dodoFeeBurnRation; + emit UpdatedodoFeeBurnRation(dodoFeeBurnRation); } - function updateDODOCirculationHelper(address _helper) public onlyOwner { - _DODO_CIRCULATION_HELPER_ = _helper; + function updateDODOCirculationHelper(address helper) public onlyOwner { + _DODO_CIRCULATION_HELPER_ = helper; } function updateGovernance(address _governance) public onlyOwner { _DOOD_GOV_ = _governance; @@ -135,66 +135,78 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { // ============ Functions ============ - function mint(uint256 _dodoAmount, address _superiorAddress) public preventReentrant { - require(_dodoAmount > 0, "vDODOToken: must deposit greater than 0"); + function mint(uint256 dodoAmount, address superiorAddress) public preventReentrant { + require(dodoAmount > 0, "vDODOToken: must mint greater than 0"); IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( _DODO_TOKEN_, msg.sender, address(this), - _dodoAmount + dodoAmount ); + uint256 newAlpha = getLatestAlpha(); - _updateAlpha(); - uint256 newVdodoAmount = DecimalMath.divFloor(_dodoAmount, alpha); + uint256 newVdodoAmount = DecimalMath.divFloor(dodoAmount, newAlpha); UserInfo storage user = userInfo[msg.sender]; _mint(user, newVdodoAmount); uint256 superiorVDODO; - if (user.superior == address(0) && _superiorAddress != address(0) && _superiorAddress != msg.sender ) { - require(_superiorAddress != msg.sender, "COULD NOT SET SELF AS SUPERIOR"); - superiorVDODO = DecimalMath.divFloor(user.VDODOAmount, _SUPERIOR_RATIO_); - user.superior = _superiorAddress; + if (user.superior == address(0) && superiorAddress != address(0)) { + require(superiorAddress != msg.sender, "COULD NOT SET SELF AS SUPERIOR"); + superiorVDODO = DecimalMath.mulFloor(user.VDODOAmount, _SUPERIOR_RATIO_); + user.superior = superiorAddress; } else if (user.superior != address(0)) { - superiorVDODO = DecimalMath.divFloor(newVdodoAmount, _SUPERIOR_RATIO_); + superiorVDODO = DecimalMath.mulFloor(newVdodoAmount, _SUPERIOR_RATIO_); } _mintToSuperior(user, superiorVDODO); + + _updateAlpha(newAlpha); - emit Deposit(msg.sender, _superiorAddress, _dodoAmount); + emit Deposit(msg.sender, superiorAddress, dodoAmount); } - function redeem(uint256 _vDodoAmount) + function redeem(uint256 vDodoAmount) public preventReentrant - balanceEnough(msg.sender, _vDodoAmount) + balanceEnough(msg.sender, vDodoAmount) { - _updateAlpha(); + uint256 newAlpha = getLatestAlpha(); UserInfo storage user = userInfo[msg.sender]; - _redeem(user, _vDodoAmount); + _redeem(user, vDodoAmount); if (user.superior != address(0)) { - uint256 superiorRedeemVDODO = DecimalMath.divFloor(_vDodoAmount, _SUPERIOR_RATIO_); + uint256 superiorRedeemVDODO = DecimalMath.mulFloor(vDodoAmount, _SUPERIOR_RATIO_); _redeemFromSuperior(user, superiorRedeemVDODO); } - (uint256 dodoReceive, uint256 destroyDodoAmount, uint256 withdrawFeeDodoAmount) = getWithdrawAmount(_vDodoAmount); + (uint256 dodoReceive, uint256 burnDodoAmount, uint256 withdrawFeeDodoAmount) = getWithdrawAmount(vDodoAmount); IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive); - _transfer(address(this), address(0), destroyDodoAmount); + _transfer(address(this), address(0), burnDodoAmount); - tmpAlpha = tmpAlpha.add(DecimalMath.divFloor(withdrawFeeDodoAmount, totalSupply)); - _updateAlpha(tmpAlpha); + newAlpha = newAlpha.add(DecimalMath.divFloor(withdrawFeeDodoAmount, totalSupply)); + _updateAlpha(newAlpha); - emit Withdraw(msg.sender, _vDodoAmount); + emit Withdraw(msg.sender, vDodoAmount); + } + + function donate(uint256 dodoAmount) public { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( + _DODO_TOKEN_, + msg.sender, + address(this), + dodoAmount + ); + alpha = alpha.add(DecimalMath.divFloor(dodoAmount, totalSupply)); } // ============ Functions(ERC20) ============ function balanceOf(address account) public view returns (uint256 balance) { UserInfo memory user = userInfo[account]; - balance = user.VDODOAmount.sub(DecimalMath.divFloor(user.credit, alpha)); + balance = user.VDODOAmount.sub(DecimalMath.divFloor(user.credit, getLatestAlpha())); } function availableBalanceOf(address account) public returns (uint256 balance) { @@ -231,56 +243,82 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { // ============ View Functions ============ - function canWithDraw(address _address) public view returns (uint256 withDrawAmount) { - UserInfo memory user = userInfo[_address]; - withDrawAmount = user.VDODOAmount.mul(alpha).sub(user.credit); + function canWithdraw(address account) public view returns (uint256 dodoAmount) { + UserInfo memory user = userInfo[account]; + dodoAmount = DecimalMath.mulFloor(user.VDODOAmount,getLatestAlpha()).sub(user.credit); } - // ============ internal function ============ - - function _updateAlpha() internal { + function getLatestAlpha() public view returns(uint256) { uint256 accuDODO = dodoPerBlock * (block.number.sub(lastRewardBlock)); if (totalSupply > 0) { - alpha = alpha.add(DecimalMath.divFloor(accuDODO, totalSupply)); + return alpha.add(DecimalMath.divFloor(accuDODO, totalSupply)); + } else { + return alpha; } + } + + function getWithdrawAmount(uint256 vDodoAmount) public view returns(uint256 dodoReceive, uint256 burnDodoAmount, uint256 withdrawFeeDodoAmount) { + uint256 feeRatio = IDODOCirculationHelper(_DODO_CIRCULATION_HELPER_).getVDODOWithdrawFeeRatio(); + + uint256 newAlpha = getLatestAlpha(); + uint256 withdrawDodoAmount = DecimalMath.mulFloor(vDodoAmount, newAlpha); + + withdrawFeeDodoAmount = DecimalMath.mulCeil(withdrawDodoAmount, feeRatio); + dodoReceive = withdrawDodoAmount.sub(withdrawFeeDodoAmount); + + if(dodoFeeBurnRation > 0){ + burnDodoAmount = DecimalMath.mulFloor(withdrawFeeDodoAmount,dodoFeeBurnRation); + withdrawFeeDodoAmount = withdrawFeeDodoAmount.sub(burnDodoAmount); + }else { + burnDodoAmount = 0; + } + } + + // ============ Internal Functions ============ + + function _updateAlpha(uint256 newAlpha) internal { + alpha = newAlpha; lastRewardBlock = block.number; } - function _mint(UserInfo storage to, uint256 amount) internal { - to.VDODOAmount = to.VDODOAmount.add(amount); - totalSupply = totalSupply.add(amount); + function _mint(UserInfo storage to, uint256 vdodoAmount) internal { + to.VDODOAmount = to.VDODOAmount.add(vdodoAmount); + totalSupply = totalSupply.add(vdodoAmount); } function _mintToSuperior(UserInfo storage user, uint256 vdodoAmount) internal { if (vdodoAmount > 0) { + uint256 newAlpha = getLatestAlpha(); + user.superiorVDODO = user.superiorVDODO.add(vdodoAmount); UserInfo storage superiorUser = userInfo[user.superior]; _mint(superiorUser, vdodoAmount); - superiorUser.credit = superiorUser.credit.add(DecimalMath.mulFloor(vdodoAmount, alpha)); + uint256 dodoAmount = DecimalMath.mulFloor(vdodoAmount, newAlpha); + superiorUser.credit = superiorUser.credit.add(DecimalMath.mulFloor(dodoAmount, _SUPERIOR_RATIO_)); } } - function _redeem(UserInfo storage from, uint256 amount) internal { - from.VDODOAmount = from.VDODOAmount.sub(amount); - totalSupply = totalSupply.sub(amount); + function _redeem(UserInfo storage from, uint256 vdodoAmount) internal { + from.VDODOAmount = from.VDODOAmount.sub(vdodoAmount); + totalSupply = totalSupply.sub(vdodoAmount); } function _redeemFromSuperior(UserInfo storage user, uint256 vdodoAmount) internal { if (vdodoAmount > 0) { - // 最多撤销当时给superior的 + uint256 newAlpha = getLatestAlpha(); + vdodoAmount = user.superiorVDODO <= vdodoAmount ? user.superiorVDODO : vdodoAmount; user.superiorVDODO = user.superiorVDODO.sub(vdodoAmount); - // 最多撤销superior的全部credit UserInfo storage superiorUser = userInfo[user.superior]; - uint256 creditVDODO = DecimalMath.divFloor(superiorUser.credit, alpha); + uint256 creditVDODO = DecimalMath.divFloor(superiorUser.credit, newAlpha); if (vdodoAmount >= creditVDODO) { superiorUser.credit = 0; _redeem(superiorUser, creditVDODO); } else { superiorUser.credit = superiorUser.credit.sub( - DecimalMath.mulFloor(vdodoAmount, alpha) + DecimalMath.mulFloor(vdodoAmount, newAlpha) ); _redeem(superiorUser, vdodoAmount); } @@ -301,47 +339,16 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { UserInfo storage toUser = userInfo[to]; toUser.VDODOAmount = toUser.VDODOAmount.add(_amount); - uint256 superiorRedeemVDODO = DecimalMath.divFloor(_amount, _SUPERIOR_RATIO_); + uint256 superiorRedeemVDODO = DecimalMath.mulFloor(_amount, _SUPERIOR_RATIO_); - address fromSuperiorAddr = fromUser.superior; - if (fromSuperiorAddr != address(0)) { + if (fromUser.superior != address(0)) { _redeemFromSuperior(fromUser, superiorRedeemVDODO); + } - address toSuperiorAddr = toUser.superior; - if (toSuperiorAddr != address(0)) { + if (toUser.superior != address(0)) { _mintToSuperior(toUser, superiorRedeemVDODO); } emit Transfer(from, to, _amount); - - } - - function getWithdrawAmount(uint256 vDodoAmount) public view returns(uint256 dodoReceive, uint256 destroyDodoAmount, uint256 withdrawFeeDodoAmount) { - - uint256 feeRatio = - IDODOCirculationHelper(_DODO_CIRCULATION_HELPER_).getVDODOWithdrawFeeRatio(); - - uint256 tmpAlpha = getAlpha(); - uint256 withdrawDodoAmount = DecimalMath.mulFloor(_vDodoAmount, tmpAlpha); - - withdrawFeeDodoAmount = DecimalMath.mulCeil(withdrawDodoAmount, feeRatio); - dodoReceive = withdrawDodoAmount.sub(withdrawFeeDodoAmount); - - if(dodoFeeDestroyRatio > 0){ - destroyDodoAmount = DecimalMath.mulCeil(withdrawDodoAmount,dodoFeeDestroyRatio).div(100); - withdrawFeeDodoAmount = withdrawFeeDodoAmount.sub(destroyDodoAmount); - }else { - destroyDodoAmount = 0; - } - - // ============ internal function ============ - function _updateAlpha(uint256 newAlpha) internal { - alpha = newAlpha; - lastRewardBlock = block.number; - } - - function donate(uint256 amount) public { - IERC20(_DODO_TOKEN_).transferFrom(msg.sender, address(this), amount); - alpha = alpha.add(DecimalMath.divFloor(amount, totalSupply)); } } From 164fa968bf58283f82db2c164bd2887edbfafcc4 Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 1 Feb 2021 18:38:15 +0800 Subject: [PATCH 20/42] fix readme --- README.md | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/README.md b/README.md index 863a2da..e14aed9 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,5 @@ # DODO V2: Help 1 Trillion People Issue Token -<<<<<<< HEAD -<<<<<<< HEAD -======= - -## Audit Report - -[Audited by Peckshield](https://github.com/DODOEX/contractV2/blob/main/audit/PeckShield-Audit-DODOV2-v1.0.pdf) - ->>>>>>> eb0b6ca13dc60f040673a1b62f1b5e6b84a3543e -## Bug Bounty 💰 - -### Rewards - -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 -<<<<<<< HEAD -======= ## Audit Report [Audited by Peckshield](https://github.com/DODOEX/contractV2/blob/main/audit/PeckShield-Audit-DODOV2-v1.0.pdf) @@ -71,7 +32,4 @@ Anyone who reports a unique, previously-unreported vulnerability that results in ## Contact Us Send E-mail to contact@dodoex.io ->>>>>>> a525e0a839159ddb4d35f4a3ea398e9f74a0df6e -======= ->>>>>>> eb0b6ca13dc60f040673a1b62f1b5e6b84a3543e From 84c44c4f42251e3ba64b19d2a799a439a0390d67 Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 1 Feb 2021 18:40:44 +0800 Subject: [PATCH 21/42] ing --- contracts/DODOToken/vDODOToken.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index 79c6c69..9a29e6e 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -97,7 +97,6 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { string memory name, string memory symbol ) public { - initOwner(msg.sender); name = name; symbol = symbol; decimals = 18; @@ -129,7 +128,8 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { function updateDODOCirculationHelper(address helper) public onlyOwner { _DODO_CIRCULATION_HELPER_ = helper; } - function updateGovernance(address _governance) public onlyOwner { + + function updateGovernance(address _governance) public onlyOwner { _DOOD_GOV_ = _governance; } From 0a1e5fc07b6eb6558d5968bfcfbcfaa64ce1d1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Tue, 2 Feb 2021 01:32:53 +0800 Subject: [PATCH 22/42] upload test case --- contracts/DODOToken/Governance.sol | 6 ++-- contracts/DODOToken/vDODOToken.sol | 6 ++-- test/Token/vdodo.test.ts | 34 ++++++++++++++++++++-- test/utils/VDODOContext.ts | 45 ++++++++++++++++++++++-------- 4 files changed, 74 insertions(+), 17 deletions(-) diff --git a/contracts/DODOToken/Governance.sol b/contracts/DODOToken/Governance.sol index 4f2371d..d06a787 100644 --- a/contracts/DODOToken/Governance.sol +++ b/contracts/DODOToken/Governance.sol @@ -13,9 +13,11 @@ contract Governance is InitializableOwnable { // ============ Storage ============ - address immutable _DODO_TOKEN_; + address _DODO_TOKEN_; - constructor(address dodoToken) public { + constructor() public { + } + function setVDODOAddress(address dodoToken) public onlyOwner{ _DODO_TOKEN_ = dodoToken; } function getLockedvDODO(address account) external pure returns (uint256 lockedvDODO) { diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index 9a29e6e..818ef3c 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -159,7 +159,9 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { superiorVDODO = DecimalMath.mulFloor(newVdodoAmount, _SUPERIOR_RATIO_); } - _mintToSuperior(user, superiorVDODO); + if (user.superior != address(0)) { + _mintToSuperior(user, superiorVDODO); + } _updateAlpha(newAlpha); @@ -209,7 +211,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { balance = user.VDODOAmount.sub(DecimalMath.divFloor(user.credit, getLatestAlpha())); } - function availableBalanceOf(address account) public returns (uint256 balance) { + function availableBalanceOf(address account) public view returns (uint256 balance) { uint256 lockedBalance = IGovernance(_DOOD_GOV_).getLockedvDODO(account); balance = balanceOf(account).sub(lockedBalance); } diff --git a/test/Token/vdodo.test.ts b/test/Token/vdodo.test.ts index 9538008..c264f09 100644 --- a/test/Token/vdodo.test.ts +++ b/test/Token/vdodo.test.ts @@ -26,6 +26,9 @@ async function init(ctx: VDODOContext): Promise { await ctx.mintTestToken(account0, decimalStr("1000")); await ctx.mintTestToken(account1, decimalStr("1000")); + await ctx.approveProxy(account0); + await ctx.approveProxy(account1); + await ctx.approveProxy(account2); } describe("VDODO", () => { @@ -48,7 +51,6 @@ describe("VDODO", () => { describe("vdodo", () => { it("vdodo init", async () => { - assert.equal( await ctx.DODO.methods.balanceOf(account0).call(), decimalStr("1000") @@ -69,7 +71,35 @@ describe("VDODO", () => { await ctx.VDODO.methods.totalSupply().call(), decimalStr("0") ); - + }); + it("vdodo first mint with no superior", async () => { + + await ctx.VDODO.methods.mint(decimalStr("10"),"0x0000000000000000000000000000000000000000").send(ctx.sendParam(account0)) + assert.equal( + await ctx.DODO.methods.balanceOf(account0).call(), + decimalStr("990") + ); + assert.equal( + await await ctx.VDODO.methods.alpha().call(), + await ctx.alpha + ); + assert.equal( + await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(), + decimalStr("10") + ); + assert.equal( + await ctx.VDODO.methods.balanceOf(account0).call(), + decimalStr("0.1") + ); + + assert.equal( + await ctx.VDODO.methods.totalSupply().call(), + decimalStr("0.1") + ); + assert.notEqual( + await ctx.VDODO.methods.lastRewardBlock().call(), + ctx.lastRewardBlock + ); }); }) }); diff --git a/test/utils/VDODOContext.ts b/test/utils/VDODOContext.ts index 3827889..4a44c0b 100644 --- a/test/utils/VDODOContext.ts +++ b/test/utils/VDODOContext.ts @@ -47,6 +47,11 @@ export class VDODOContext { this.EVM = new EVM(); this.Web3 = getDefaultWeb3(); + const allAccounts = await this.Web3.eth.getAccounts(); + this.Deployer = allAccounts[0]; + this.Maintainer = allAccounts[1]; + this.SpareAccounts = allAccounts.slice(2, 10); + this.DODO = await contracts.newContract( contracts.MINTABLE_ERC20_CONTRACT_NAME, ["DODO Token", "DODO", 18] @@ -67,9 +72,14 @@ export class VDODOContext { [this.DODOApprove.options.address] ) + + this.Governance = await contracts.newContract( + contracts.DODO_GOVERNANCE + ) this.VDODO = await contracts.newContract( contracts.VDODO_NAME, [ + this.Governance.options.address, this.DODO.options.address, this.DODOCirculationHelper.options.address, this.DODOApproveProxy.options.address, @@ -77,26 +87,31 @@ export class VDODOContext { ] ) - this.Governance = await contracts.newContract( - contracts.DODO_GOVERNANCE, - [this.VDODO.options.address] - ) + await this.Governance.methods.initOwner( + this.Deployer + ).send(this.sendParam(this.Deployer)) - const allAccounts = await this.Web3.eth.getAccounts(); - this.Deployer = allAccounts[0]; - this.Maintainer = allAccounts[1]; - this.SpareAccounts = allAccounts.slice(2, 10); + await this.Governance.methods.setVDODOAddress( + this.VDODO.options.address + ).send(this.sendParam(this.Deployer)) - await this.VDODO.methods.updateGovernance( - this.Governance.options.address + await this.DODOApprove.methods.init(this.Deployer,this.DODOApproveProxy.options.address).send(this.sendParam(this.Deployer)); + await this.DODOApproveProxy.methods.init(this.Deployer, [this.VDODO.options.address]).send(this.sendParam(this.Deployer)); + + + + + await this.VDODO.methods.initOwner( + this.Deployer ).send(this.sendParam(this.Deployer)) this.alpha = await this.VDODO.methods.alpha().call(); this.lastRewardBlock = await this.VDODO.methods.lastRewardBlock().call(); + console.log(log.blueText("[Init VDODO context]")); + console.log("alpha = "+ this.alpha); console.log("lastRewardBlock = " + this.lastRewardBlock); - console.log(log.blueText("[Init VDODO context]")); } sendParam(sender, value = "0") { @@ -111,6 +126,14 @@ export class VDODOContext { async mintTestToken(to: string, amount: string) { await this.DODO.methods.mint(to, amount).send(this.sendParam(this.Deployer)); } + async approveProxy(account: string) { + await this.DODO.methods + .approve(this.DODOApprove.options.address, MAX_UINT256) + .send(this.sendParam(account)); + await this.VDODO.methods + .approve(this.DODOApprove.options.address, MAX_UINT256) + .send(this.sendParam(account)); + } } export async function getVDODOContext(): Promise { From b698218993b1a6d62d732eb1eca042013d933d37 Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 2 Feb 2021 14:35:05 +0800 Subject: [PATCH 23/42] update vdodo --- contracts/DODOToken/vDODOToken.sol | 128 +++++++++++++++-------------- deploy-detail-v1.5.txt | 8 ++ deploy-detail-v2.0.txt | 14 ++++ migrations/3_deploy_v2.js | 6 +- truffle-config.js | 2 +- 5 files changed, 91 insertions(+), 67 deletions(-) diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index 818ef3c..ce6f437 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -52,21 +52,21 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { uint256 public dodoFeeBurnRation; // accounting - uint256 public alpha = 100 * 10**18; // 100 - uint256 public lastRewardBlock; + uint128 public alpha = 100 * 10**18; // 100 + uint128 public lastRewardBlock; mapping(address => UserInfo) public userInfo; struct UserInfo { - uint256 VDODOAmount; - uint256 credit; + uint128 VDODOAmount; + uint128 superiorVDODO; address superior; - uint256 superiorVDODO; + uint256 credit; } // ============ Events ============ - event Deposit(address user, address superior, uint256 amount); - event Withdraw(address user, uint256 amount); + event MintVDODO(address user, address superior, uint256 amount); + event RedeemVDODO(address user, uint256 amount); event SetCantransfer(bool allowed); event ChangePerReward(uint256 dodoPerBlock); @@ -94,17 +94,17 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { address dodoToken, address dodoCirculationHelper, address dodoApproveProxy, - string memory name, - string memory symbol + string memory _name, + string memory _symbol ) public { - name = name; - symbol = symbol; + name = _name; + symbol = _symbol; decimals = 18; _DODO_APPROVE_PROXY_ = dodoApproveProxy; _DOOD_GOV_ = dodoGov; _DODO_CIRCULATION_HELPER_ = dodoCirculationHelper; _DODO_TOKEN_ = dodoToken; - lastRewardBlock = block.number; + lastRewardBlock = uint128(block.number); } // ============ Ownable Functions ============` @@ -114,58 +114,54 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { emit SetCantransfer(allowed); } - function changePerReward(uint256 dodoPerBlock) public onlyOwner { - _updateAlpha(getLatestAlpha()); - dodoPerBlock = dodoPerBlock; - emit ChangePerReward(dodoPerBlock); + function changePerReward(uint256 _dodoPerBlock) public onlyOwner { + _updateAlpha(); + dodoPerBlock = _dodoPerBlock; + emit ChangePerReward(_dodoPerBlock); } - function updatedodoFeeBurnRation(uint256 dodoFeeBurnRation) public onlyOwner { - dodoFeeBurnRation = dodoFeeBurnRation; - emit UpdatedodoFeeBurnRation(dodoFeeBurnRation); + function updatedodoFeeBurnRation(uint256 _dodoFeeBurnRation) public onlyOwner { + dodoFeeBurnRation = _dodoFeeBurnRation; + emit UpdatedodoFeeBurnRation(_dodoFeeBurnRation); } function updateDODOCirculationHelper(address helper) public onlyOwner { _DODO_CIRCULATION_HELPER_ = helper; } - function updateGovernance(address _governance) public onlyOwner { - _DOOD_GOV_ = _governance; + function updateGovernance(address governance) public onlyOwner { + _DOOD_GOV_ = governance; } // ============ Functions ============ function mint(uint256 dodoAmount, address superiorAddress) public preventReentrant { + require(superiorAddress != address(0) && superiorAddress != msg.sender, "vDODOToken: Superior INVALID"); require(dodoAmount > 0, "vDODOToken: must mint greater than 0"); + + _updateAlpha(); + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( _DODO_TOKEN_, msg.sender, address(this), dodoAmount ); - uint256 newAlpha = getLatestAlpha(); - uint256 newVdodoAmount = DecimalMath.divFloor(dodoAmount, newAlpha); + uint256 newVdodoAmount = DecimalMath.divFloor(dodoAmount, alpha); UserInfo storage user = userInfo[msg.sender]; _mint(user, newVdodoAmount); - uint256 superiorVDODO; - if (user.superior == address(0) && superiorAddress != address(0)) { - require(superiorAddress != msg.sender, "COULD NOT SET SELF AS SUPERIOR"); - superiorVDODO = DecimalMath.mulFloor(user.VDODOAmount, _SUPERIOR_RATIO_); - user.superior = superiorAddress; - } else if (user.superior != address(0)) { - superiorVDODO = DecimalMath.mulFloor(newVdodoAmount, _SUPERIOR_RATIO_); - } - - if (user.superior != address(0)) { - _mintToSuperior(user, superiorVDODO); - } + uint256 increSuperiorVDODO = DecimalMath.mulFloor(newVdodoAmount, _SUPERIOR_RATIO_); - _updateAlpha(newAlpha); + if (user.superior == address(0)) { + user.superior = superiorAddress; + } - emit Deposit(msg.sender, superiorAddress, dodoAmount); + _mintToSuperior(user, increSuperiorVDODO); + + emit MintVDODO(msg.sender, superiorAddress, dodoAmount); } function redeem(uint256 vDodoAmount) @@ -173,7 +169,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { preventReentrant balanceEnough(msg.sender, vDodoAmount) { - uint256 newAlpha = getLatestAlpha(); + _updateAlpha(); UserInfo storage user = userInfo[msg.sender]; _redeem(user, vDodoAmount); @@ -185,15 +181,20 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { (uint256 dodoReceive, uint256 burnDodoAmount, uint256 withdrawFeeDodoAmount) = getWithdrawAmount(vDodoAmount); - IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive); - _transfer(address(this), address(0), burnDodoAmount); + IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive); - newAlpha = newAlpha.add(DecimalMath.divFloor(withdrawFeeDodoAmount, totalSupply)); - _updateAlpha(newAlpha); + if(burnDodoAmount > 0){ + _transfer(address(this), address(0), burnDodoAmount); + } - emit Withdraw(msg.sender, vDodoAmount); + if(withdrawFeeDodoAmount > 0) { + alpha = uint128(uint256(alpha).add(DecimalMath.divFloor(withdrawFeeDodoAmount, totalSupply))); + } + + emit RedeemVDODO(msg.sender, vDodoAmount); } + function donate(uint256 dodoAmount) public { IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( _DODO_TOKEN_, @@ -201,14 +202,14 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { address(this), dodoAmount ); - alpha = alpha.add(DecimalMath.divFloor(dodoAmount, totalSupply)); + alpha = uint128(uint256(alpha).add(DecimalMath.divFloor(dodoAmount, totalSupply))); } // ============ Functions(ERC20) ============ function balanceOf(address account) public view returns (uint256 balance) { UserInfo memory user = userInfo[account]; - balance = user.VDODOAmount.sub(DecimalMath.divFloor(user.credit, getLatestAlpha())); + balance = uint256(user.VDODOAmount).sub(DecimalMath.divFloor(user.credit, getLatestAlpha())); } function availableBalanceOf(address account) public view returns (uint256 balance) { @@ -217,6 +218,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { } function transfer(address to, uint256 amount) public returns (bool) { + _updateAlpha(); _transfer(msg.sender, to, amount); return true; } @@ -233,6 +235,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { uint256 amount ) public returns (bool) { require(amount <= _ALLOWED_[from][msg.sender], "ALLOWANCE_NOT_ENOUGH"); + _updateAlpha(); _transfer(from, to, amount); _ALLOWED_[from][msg.sender] = _ALLOWED_[from][msg.sender].sub(amount); emit Transfer(from, to, amount); @@ -247,13 +250,13 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { function canWithdraw(address account) public view returns (uint256 dodoAmount) { UserInfo memory user = userInfo[account]; - dodoAmount = DecimalMath.mulFloor(user.VDODOAmount,getLatestAlpha()).sub(user.credit); + dodoAmount = DecimalMath.mulFloor(uint256(user.VDODOAmount),getLatestAlpha()).sub(user.credit); } function getLatestAlpha() public view returns(uint256) { uint256 accuDODO = dodoPerBlock * (block.number.sub(lastRewardBlock)); if (totalSupply > 0) { - return alpha.add(DecimalMath.divFloor(accuDODO, totalSupply)); + return uint256(alpha).add(DecimalMath.divFloor(accuDODO, totalSupply)); } else { return alpha; } @@ -278,49 +281,48 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { // ============ Internal Functions ============ - function _updateAlpha(uint256 newAlpha) internal { - alpha = newAlpha; - lastRewardBlock = block.number; + function _updateAlpha() internal { + uint256 newAlpha = getLatestAlpha(); + require(newAlpha <= uint128(-1), "OVERFLOW"); + alpha = uint128(newAlpha); + lastRewardBlock = uint128(block.number); } function _mint(UserInfo storage to, uint256 vdodoAmount) internal { - to.VDODOAmount = to.VDODOAmount.add(vdodoAmount); + require(vdodoAmount <= uint128(-1), "OVERFLOW"); + to.VDODOAmount = uint128(uint256(to.VDODOAmount).add(vdodoAmount)); totalSupply = totalSupply.add(vdodoAmount); } function _mintToSuperior(UserInfo storage user, uint256 vdodoAmount) internal { if (vdodoAmount > 0) { - uint256 newAlpha = getLatestAlpha(); - - user.superiorVDODO = user.superiorVDODO.add(vdodoAmount); + user.superiorVDODO = uint128(uint256(user.superiorVDODO).add(vdodoAmount)); UserInfo storage superiorUser = userInfo[user.superior]; _mint(superiorUser, vdodoAmount); - uint256 dodoAmount = DecimalMath.mulFloor(vdodoAmount, newAlpha); + uint256 dodoAmount = DecimalMath.mulFloor(vdodoAmount, alpha); superiorUser.credit = superiorUser.credit.add(DecimalMath.mulFloor(dodoAmount, _SUPERIOR_RATIO_)); } } function _redeem(UserInfo storage from, uint256 vdodoAmount) internal { - from.VDODOAmount = from.VDODOAmount.sub(vdodoAmount); + from.VDODOAmount = uint128(uint256(from.VDODOAmount).sub(vdodoAmount)); totalSupply = totalSupply.sub(vdodoAmount); } function _redeemFromSuperior(UserInfo storage user, uint256 vdodoAmount) internal { if (vdodoAmount > 0) { - uint256 newAlpha = getLatestAlpha(); - vdodoAmount = user.superiorVDODO <= vdodoAmount ? user.superiorVDODO : vdodoAmount; - user.superiorVDODO = user.superiorVDODO.sub(vdodoAmount); + user.superiorVDODO = uint128(uint256(user.superiorVDODO).sub(vdodoAmount)); UserInfo storage superiorUser = userInfo[user.superior]; - uint256 creditVDODO = DecimalMath.divFloor(superiorUser.credit, newAlpha); + uint256 creditVDODO = DecimalMath.divFloor(superiorUser.credit, alpha); if (vdodoAmount >= creditVDODO) { superiorUser.credit = 0; _redeem(superiorUser, creditVDODO); } else { superiorUser.credit = superiorUser.credit.sub( - DecimalMath.mulFloor(vdodoAmount, newAlpha) + DecimalMath.mulFloor(vdodoAmount, alpha) ); _redeem(superiorUser, vdodoAmount); } @@ -336,10 +338,10 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { require(to != address(0), "transfer to the zero address"); UserInfo storage fromUser = userInfo[from]; - fromUser.VDODOAmount = fromUser.VDODOAmount.sub(_amount); + fromUser.VDODOAmount = uint128(uint256(fromUser.VDODOAmount).sub(_amount)); UserInfo storage toUser = userInfo[to]; - toUser.VDODOAmount = toUser.VDODOAmount.add(_amount); + toUser.VDODOAmount = uint128(uint256(toUser.VDODOAmount).add(_amount)); uint256 superiorRedeemVDODO = DecimalMath.mulFloor(_amount, _SUPERIOR_RATIO_); diff --git a/deploy-detail-v1.5.txt b/deploy-detail-v1.5.txt index 10ef18c..488512c 100644 --- a/deploy-detail-v1.5.txt +++ b/deploy-detail-v1.5.txt @@ -112,3 +112,11 @@ Deploy time: 2021/1/26 下午5:41:41 Deploy type: Proxy DODOV1Proxy04 Address: 0xa2CB66EBB947D217f61510882096F6e95c1DE97D Set DODOProxy Owner tx: 0xe49234c67bc710a7f4797aacbb2ef26fac458832f101e856d8113cc21721cd81 +==================================================== +network type: kovan +Deploy time: 2021/2/1 下午11:07:47 +Deploy type: Proxy +==================================================== +network type: kovan +Deploy time: 2021/2/1 下午11:08:57 +Deploy type: Proxy diff --git a/deploy-detail-v2.0.txt b/deploy-detail-v2.0.txt index 83b513d..eefcc3e 100644 --- a/deploy-detail-v2.0.txt +++ b/deploy-detail-v2.0.txt @@ -542,3 +542,17 @@ Deploy time: 2021/1/26 下午5:29:51 ==================================================== network type: live Deploy time: 2021/1/26 下午5:42:23 +==================================================== +network type: kovan +Deploy time: 2021/2/1 下午11:09:38 +Deploy type: V2 +DODOApprove Address: 0x929ce7b28aA759a1C0E6e855ff2545f9e5ca846A +DODOApproveProxy Address: 0x8a61c59e2DE32af51A252d754c7f852aA44191C8 +DODOV2Proxy02 Address: 0xf47BEA4D26026810787b56670502E058CEe5c386 +Init DODOProxyV2 Tx: 0x48c4de1d6008c96d82a92da63747385b218b56fe3451d4256b9b8429623d34f4 +DODOApproveProxy Init tx: 0xb12443d847d426fbeed3c0c3d0e796d52b214ebff65ca604a72dd7400b94e42c +DODOApprove Init tx: 0x26d3f4be069eccf94b0b6d3ee1db07a534cadc98c4dd2b96fd5921d00f57403c +DODOIncentive ChangeProxy tx: 0x9564307e19b26aed9980d9ac007dc88b789450893540426618de4f1039f5584f +==================================================== +network type: kovan +Deploy time: 2021/2/2 下午2:28:36 diff --git a/migrations/3_deploy_v2.js b/migrations/3_deploy_v2.js index 0013aef..946a977 100644 --- a/migrations/3_deploy_v2.js +++ b/migrations/3_deploy_v2.js @@ -85,7 +85,7 @@ module.exports = async (deployer, network, accounts) => { DefaultPermissionAddress = "0xACc7E23368261e1E02103c4e5ae672E7D01f5797"; DvmTemplateAddress = "0xA6384D1501842e9907D43148E2ca0d50E4ad56E2"; - DppTemplateAddress = "0x044b48D64E77Ab8854C46c8456dC05C540c9dd53"; + DppTemplateAddress = ""; DppAdminTemplateAddress = ""; CpTemplateAddress = "0x81c802080c3CE0dE98fcb625670A14Eb8440184a"; //Factory @@ -379,8 +379,8 @@ module.exports = async (deployer, network, accounts) => { logger.log("DODOIncentive ChangeProxy tx: ", tx.tx); //3. Open trade incentive - var tx = await DODOIncentiveInstance.changePerReward("10000000000000000000"); - logger.log("DODOIncentive OpenSwitch tx: ", tx.tx); + // var tx = await DODOIncentiveInstance.changePerReward("10000000000000000000"); + // logger.log("DODOIncentive OpenSwitch tx: ", tx.tx); //4. Transfer DODO to Trade Incentive } diff --git a/truffle-config.js b/truffle-config.js index 5559589..e2ad8f4 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -38,7 +38,7 @@ module.exports = { * $ truffle test --network */ deploySwitch: { - DEPLOY_V1: true, + DEPLOY_V1: false, DEPLOY_V2: false, ADAPTER: false, MOCK_TOKEN: false, From c88fca7dcee5cfb08eddf10c4ca6d2afbf3e2299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Tue, 2 Feb 2021 14:58:38 +0800 Subject: [PATCH 24/42] update test case --- test/Token/vdodo.test.ts | 89 ++++++++++++++++++++++++++------------ test/utils/VDODOContext.ts | 6 +-- 2 files changed, 65 insertions(+), 30 deletions(-) diff --git a/test/Token/vdodo.test.ts b/test/Token/vdodo.test.ts index c264f09..1f210a7 100644 --- a/test/Token/vdodo.test.ts +++ b/test/Token/vdodo.test.ts @@ -72,34 +72,69 @@ describe("VDODO", () => { decimalStr("0") ); }); - it("vdodo first mint with no superior", async () => { + // it("vdodo first mint with no superior", async () => { - await ctx.VDODO.methods.mint(decimalStr("10"),"0x0000000000000000000000000000000000000000").send(ctx.sendParam(account0)) - assert.equal( - await ctx.DODO.methods.balanceOf(account0).call(), - decimalStr("990") - ); - assert.equal( - await await ctx.VDODO.methods.alpha().call(), - await ctx.alpha - ); - assert.equal( - await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(), - decimalStr("10") - ); - assert.equal( - await ctx.VDODO.methods.balanceOf(account0).call(), - decimalStr("0.1") - ); + // await ctx.VDODO.methods.mint(decimalStr("10"),"0x0000000000000000000000000000000000000000").send(ctx.sendParam(account0)) + // assert.equal( + // await ctx.DODO.methods.balanceOf(account0).call(), + // decimalStr("990") + // ); + // assert.equal( + // await await ctx.VDODO.methods.alpha().call(), + // await ctx.alpha + // ); + // assert.equal( + // await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(), + // decimalStr("10") + // ); + // assert.equal( + // await ctx.VDODO.methods.balanceOf(account0).call(), + // decimalStr("0.1") + // ); - assert.equal( - await ctx.VDODO.methods.totalSupply().call(), - decimalStr("0.1") - ); - assert.notEqual( - await ctx.VDODO.methods.lastRewardBlock().call(), - ctx.lastRewardBlock - ); - }); + // assert.equal( + // await ctx.VDODO.methods.totalSupply().call(), + // decimalStr("0.1") + // ); + // assert.notEqual( + // await ctx.VDODO.methods.lastRewardBlock().call(), + // ctx.lastRewardBlock + // ); + // }); + // it("vdodo owner can transfer", async () => { + + // await ctx.VDODO.methods.mint(decimalStr("10"),"0x0000000000000000000000000000000000000000").send(ctx.sendParam(account0)) + // assert.equal( + // await ctx.DODO.methods.balanceOf(account0).call(), + // decimalStr("990") + // ); + // assert.equal( + // await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(), + // decimalStr("10") + // ); + // assert.equal( + // await ctx.VDODO.methods.balanceOf(account0).call(), + // decimalStr("0.1") + // ); + // assert.equal( + // await ctx.VDODO.methods.balanceOf(account1).call(), + // decimalStr("0") + // ); + + + // await truffleAssert.reverts( + // ctx.VDODO.methods.transfer(account1,decimalStr("0.1")).send(ctx.sendParam(account0)), + // "vDODOToken: not allowed transfer" + // ) + // assert.equal( + // await ctx.VDODO.methods.balanceOf(account0).call(), + // decimalStr("0.1") + // ); + // assert.equal( + // await ctx.VDODO.methods.balanceOf(account1).call(), + // decimalStr("0") + // ); + + // }); }) }); diff --git a/test/utils/VDODOContext.ts b/test/utils/VDODOContext.ts index 4a44c0b..8c447ac 100644 --- a/test/utils/VDODOContext.ts +++ b/test/utils/VDODOContext.ts @@ -109,9 +109,9 @@ export class VDODOContext { this.lastRewardBlock = await this.VDODO.methods.lastRewardBlock().call(); console.log(log.blueText("[Init VDODO context]")); - - console.log("alpha = "+ this.alpha); - console.log("lastRewardBlock = " + this.lastRewardBlock); + + console.log("init alpha = " + this.alpha); + console.log("init lastRewardBlock = " + this.lastRewardBlock); } sendParam(sender, value = "0") { From 70d00a5c0afcf329cfc88989eff962a73f7c1c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Tue, 2 Feb 2021 15:01:28 +0800 Subject: [PATCH 25/42] add canTransfer --- contracts/DODOToken/vDODOToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index ce6f437..a143596 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -333,7 +333,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { address from, address to, uint256 _amount - ) internal balanceEnough(msg.sender, _amount) { + ) internal balanceEnough(msg.sender, _amount) canTransfer { require(from != address(0), "transfer from the zero address"); require(to != address(0), "transfer to the zero address"); From 75fe061db96ac4bbbaa03acab71353d55e0581e1 Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 2 Feb 2021 15:24:57 +0800 Subject: [PATCH 26/42] ing --- contracts/DODOToken/Governance.sol | 2 ++ deploy-detail-v2.0.txt | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/contracts/DODOToken/Governance.sol b/contracts/DODOToken/Governance.sol index d06a787..8cca54d 100644 --- a/contracts/DODOToken/Governance.sol +++ b/contracts/DODOToken/Governance.sol @@ -17,9 +17,11 @@ contract Governance is InitializableOwnable { constructor() public { } + function setVDODOAddress(address dodoToken) public onlyOwner{ _DODO_TOKEN_ = dodoToken; } + function getLockedvDODO(address account) external pure returns (uint256 lockedvDODO) { lockedvDODO = 0;//DOTO,0 for test } diff --git a/deploy-detail-v2.0.txt b/deploy-detail-v2.0.txt index eefcc3e..894105c 100644 --- a/deploy-detail-v2.0.txt +++ b/deploy-detail-v2.0.txt @@ -556,3 +556,19 @@ DODOIncentive ChangeProxy tx: 0x9564307e19b26aed9980d9ac007dc88b789450893540426 ==================================================== network type: kovan Deploy time: 2021/2/2 下午2:28:36 +==================================================== +network type: kovan +Deploy time: 2021/2/2 下午2:38:26 +Deploy type: V2 +DppTemplateAddress: 0x6b9Db3908ddFD853AD2A42Ab75b5de3f22f137a5 +DppAdminTemplateAddress: 0x2d69731283ac620760309d8b561De11C6166a5F5 +DODOApprove Address: 0x7BD4dBc1F3cd0333E910aa9eeA14A2d96e3684C2 +DODOApproveProxy Address: 0x0cB68c60E4C2F023C4fdbC6Cbe283aAb34866e8a +DppFactoryAddress: 0x80c03749C22Acbe5EaFEb1d550a32C707a67fc34 +Init DppFactory Tx: 0x643c1fdcefe736f782f0c1be2478ff09515030eb9e6c6030f0ec9513bd327bf7 +DODOV2RouteHelper Address: 0xD5171044E369Ef316125da5A0Af8E75ea6Cd3A90 +DODOV2Proxy02 Address: 0xFC6840905E7E550A209B3856f313C523D2f9a3e3 +Init DODOProxyV2 Tx: 0xb41219ef20f0496c8c2088bf8d30a64dd3fa1ef5cdaeaa9161a6da52349dd76d +DODOApproveProxy Init tx: 0x38ced5f643938891ad553b40bafe55ffc6065a15e4bbc81f4cdfbe4f3a0b9494 +DODOApprove Init tx: 0x7eac3f1fa6dbe499351ea52066623980615bdc7901a84f2afa8fc67e34e4be59 +DODOIncentive ChangeProxy tx: 0x6d64bae7ee09f12db0bfc18823ac8e2ca1d682184c1ed3cc157f2b39641409e3 From 126acd6bdf97619951412163ccc621204cebe0b7 Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 2 Feb 2021 16:16:21 +0800 Subject: [PATCH 27/42] vDODO test frameWork update --- contracts/DODOToken/Governance.sol | 9 +-- test/utils/VDODOContext.ts | 22 +++--- test/vDODO/erc20.test.ts | 68 +++++++++++++++++++ test/vDODO/global.test.ts | 64 +++++++++++++++++ .../mintRedeem.test.ts} | 52 ++++++++++++-- 5 files changed, 193 insertions(+), 22 deletions(-) create mode 100644 test/vDODO/erc20.test.ts create mode 100644 test/vDODO/global.test.ts rename test/{Token/vdodo.test.ts => vDODO/mintRedeem.test.ts} (76%) diff --git a/contracts/DODOToken/Governance.sol b/contracts/DODOToken/Governance.sol index 8cca54d..9c950d2 100644 --- a/contracts/DODOToken/Governance.sol +++ b/contracts/DODOToken/Governance.sol @@ -13,15 +13,12 @@ contract Governance is InitializableOwnable { // ============ Storage ============ - address _DODO_TOKEN_; + address immutable _DODO_TOKEN_; - constructor() public { + constructor(dodoToken) public { + _DODO_TOKEN_ = dodoToken } - function setVDODOAddress(address dodoToken) public onlyOwner{ - _DODO_TOKEN_ = dodoToken; - } - function getLockedvDODO(address account) external pure returns (uint256 lockedvDODO) { lockedvDODO = 0;//DOTO,0 for test } diff --git a/test/utils/VDODOContext.ts b/test/utils/VDODOContext.ts index 8c447ac..e7ea044 100644 --- a/test/utils/VDODOContext.ts +++ b/test/utils/VDODOContext.ts @@ -72,10 +72,13 @@ export class VDODOContext { [this.DODOApprove.options.address] ) - this.Governance = await contracts.newContract( - contracts.DODO_GOVERNANCE + contracts.DODO_GOVERNANCE, + [ + this.DODO.options.address + ] ) + this.VDODO = await contracts.newContract( contracts.VDODO_NAME, [ @@ -91,23 +94,20 @@ export class VDODOContext { this.Deployer ).send(this.sendParam(this.Deployer)) - await this.Governance.methods.setVDODOAddress( - this.VDODO.options.address - ).send(this.sendParam(this.Deployer)) - await this.DODOApprove.methods.init(this.Deployer,this.DODOApproveProxy.options.address).send(this.sendParam(this.Deployer)); await this.DODOApproveProxy.methods.init(this.Deployer, [this.VDODO.options.address]).send(this.sendParam(this.Deployer)); - - await this.VDODO.methods.initOwner( this.Deployer ).send(this.sendParam(this.Deployer)) + await this.VDODO.methods.changePerReward(decimalStr("1")).send(this.sendParam(this.Deployer)); + await this.mintTestToken(this.VDODO.options.address, decimalStr("100000")); + this.alpha = await this.VDODO.methods.alpha().call(); this.lastRewardBlock = await this.VDODO.methods.lastRewardBlock().call(); - + console.log(log.blueText("[Init VDODO context]")); console.log("init alpha = " + this.alpha); @@ -126,13 +126,11 @@ export class VDODOContext { async mintTestToken(to: string, amount: string) { await this.DODO.methods.mint(to, amount).send(this.sendParam(this.Deployer)); } + async approveProxy(account: string) { await this.DODO.methods .approve(this.DODOApprove.options.address, MAX_UINT256) .send(this.sendParam(account)); - await this.VDODO.methods - .approve(this.DODOApprove.options.address, MAX_UINT256) - .send(this.sendParam(account)); } } diff --git a/test/vDODO/erc20.test.ts b/test/vDODO/erc20.test.ts new file mode 100644 index 0000000..ff0d4a5 --- /dev/null +++ b/test/vDODO/erc20.test.ts @@ -0,0 +1,68 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +import { decimalStr, MAX_UINT256 } from '../utils/Converter'; +import { logGas } from '../utils/Log'; +import { VDODOContext, getVDODOContext } from '../utils/VDODOContext'; +import { assert } from 'chai'; +import BigNumber from 'bignumber.js'; + +let account0: string; +let account1: string; + +async function init(ctx: VDODOContext): Promise { + account0 = ctx.SpareAccounts[0]; + account1 = ctx.SpareAccounts[1]; + + await ctx.mintTestToken(account0, decimalStr("1000")); + await ctx.mintTestToken(account1, decimalStr("1000")); + + await ctx.approveProxy(account0); + await ctx.approveProxy(account1); +} + +describe("vDODO-erc20", () => { + let snapshotId: string; + let ctx: VDODOContext; + + before(async () => { + ctx = await getVDODOContext(); + //打开transfer开关 + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("vdodo-erc20", () => { + + it("transfer-vdodo", async () => { + //检查四个人 【包括from, to 以及各自的上级】,info变化 + //alpha lastRewardBlock + //各自dodo余额变化 + }); + + it("transferFrom-vdodo", async () => { + //检查四个人 【包括from, to 以及各自的上级】,info变化 + //alpha lastRewardBlock + //各自dodo余额变化 + //approve 状态变化 + + + //再次transferFrom 预期revert + }); + + it("transfer - close", async () => { + //预期revert + }); + }) +}); diff --git a/test/vDODO/global.test.ts b/test/vDODO/global.test.ts new file mode 100644 index 0000000..41d5afa --- /dev/null +++ b/test/vDODO/global.test.ts @@ -0,0 +1,64 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +import { decimalStr, MAX_UINT256 } from '../utils/Converter'; +import { logGas } from '../utils/Log'; +import { VDODOContext, getVDODOContext } from '../utils/VDODOContext'; +import { assert } from 'chai'; +import BigNumber from 'bignumber.js'; + +let account0: string; +let account1: string; + +async function init(ctx: VDODOContext): Promise { + account0 = ctx.SpareAccounts[0]; + account1 = ctx.SpareAccounts[1]; + + await ctx.mintTestToken(account0, decimalStr("1000")); + await ctx.mintTestToken(account1, decimalStr("1000")); + + await ctx.approveProxy(account0); + await ctx.approveProxy(account1); +} + +describe("vDODO-owner", () => { + let snapshotId: string; + let ctx: VDODOContext; + + before(async () => { + ctx = await getVDODOContext(); + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("vdodo-erc20", () => { + + it("change-reward", async () => { + //改变前alpha lastRewardBlock 状态 + + //change-reward + + //改变后状态 + }); + + + it("donate", async () => { + //改变前alpha lastRewardBlock 状态 + + //change-reward + + //改变后状态 + }); + }) +}); diff --git a/test/Token/vdodo.test.ts b/test/vDODO/mintRedeem.test.ts similarity index 76% rename from test/Token/vdodo.test.ts rename to test/vDODO/mintRedeem.test.ts index 1f210a7..2474472 100644 --- a/test/Token/vdodo.test.ts +++ b/test/vDODO/mintRedeem.test.ts @@ -12,25 +12,23 @@ import { logGas } from '../utils/Log'; import { VDODOContext, getVDODOContext } from '../utils/VDODOContext'; import { assert } from 'chai'; import BigNumber from 'bignumber.js'; -const truffleAssert = require('truffle-assertions'); let account0: string; let account1: string; -let account2: string; async function init(ctx: VDODOContext): Promise { account0 = ctx.SpareAccounts[0]; account1 = ctx.SpareAccounts[1]; - account2 = ctx.SpareAccounts[2]; await ctx.mintTestToken(account0, decimalStr("1000")); await ctx.mintTestToken(account1, decimalStr("1000")); await ctx.approveProxy(account0); await ctx.approveProxy(account1); - await ctx.approveProxy(account2); } +//TODO: 抽象出来mint func + describe("VDODO", () => { let snapshotId: string; let ctx: VDODOContext; @@ -72,6 +70,52 @@ describe("VDODO", () => { decimalStr("0") ); }); + + + it("vdodo-mint-first", async () => { + //第一次mint 后 + //alpha lastRewardBlock 状态 + //user superior info 状态 + //vDODO 总量变化,以及vDODO合约dodo余额,user dodo余额 + + }); + + it("vdodo-mint-second", async () => { + //第二次mint 后(传入之前的superior地址) + //alpha lastRewardBlock 状态 + //user superior info 状态 + //vDODO 总量变化,以及vDODO合约dodo余额,user dodo余额 + }); + + + it("vdodo-mint-second-otherSuperior", async () => { + //第二次mint 后(传入非之前superior地址) + //alpha lastRewardBlock 状态 + //user superior info 状态 + //vDODO 总量变化,以及vDODO合约dodo余额,user dodo余额 + }); + + + it("redeem-amount-read", async () => { + //正确读取 withdrawAmount 字段 + }); + + it("redeem-partial-haveMint", async () => { + + }); + + it("redeem-partial-NotMint", async () => { + //多个下级引用 + }); + + it("redeem-all-haveMint", async () => { + + }); + + it("redeem-all-NoMint", async () => { + //多个下级引用 + }); + // it("vdodo first mint with no superior", async () => { // await ctx.VDODO.methods.mint(decimalStr("10"),"0x0000000000000000000000000000000000000000").send(ctx.sendParam(account0)) From d9a7d41907c747ae184c55a7a79936e2e15e99e8 Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 2 Feb 2021 16:30:00 +0800 Subject: [PATCH 28/42] update --- test/vDODO/erc20.test.ts | 37 +++++++++++++++++++++ test/vDODO/global.test.ts | 4 +++ test/vDODO/mintRedeem.test.ts | 60 ++--------------------------------- 3 files changed, 43 insertions(+), 58 deletions(-) diff --git a/test/vDODO/erc20.test.ts b/test/vDODO/erc20.test.ts index ff0d4a5..783dd87 100644 --- a/test/vDODO/erc20.test.ts +++ b/test/vDODO/erc20.test.ts @@ -64,5 +64,42 @@ describe("vDODO-erc20", () => { it("transfer - close", async () => { //预期revert }); + + + // it("vdodo owner can transfer", async () => { + + // await ctx.VDODO.methods.mint(decimalStr("10"),"0x0000000000000000000000000000000000000000").send(ctx.sendParam(account0)) + // assert.equal( + // await ctx.DODO.methods.balanceOf(account0).call(), + // decimalStr("990") + // ); + // assert.equal( + // await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(), + // decimalStr("10") + // ); + // assert.equal( + // await ctx.VDODO.methods.balanceOf(account0).call(), + // decimalStr("0.1") + // ); + // assert.equal( + // await ctx.VDODO.methods.balanceOf(account1).call(), + // decimalStr("0") + // ); + + + // await truffleAssert.reverts( + // ctx.VDODO.methods.transfer(account1,decimalStr("0.1")).send(ctx.sendParam(account0)), + // "vDODOToken: not allowed transfer" + // ) + // assert.equal( + // await ctx.VDODO.methods.balanceOf(account0).call(), + // decimalStr("0.1") + // ); + // assert.equal( + // await ctx.VDODO.methods.balanceOf(account1).call(), + // decimalStr("0") + // ); + + // }); }) }); diff --git a/test/vDODO/global.test.ts b/test/vDODO/global.test.ts index 41d5afa..6d7584a 100644 --- a/test/vDODO/global.test.ts +++ b/test/vDODO/global.test.ts @@ -60,5 +60,9 @@ describe("vDODO-owner", () => { //改变后状态 }); + + it("read-helper", async () => { + //不同amount对应的feeRatio (5 5-15 15) + }); }) }); diff --git a/test/vDODO/mintRedeem.test.ts b/test/vDODO/mintRedeem.test.ts index 2474472..24bd0fe 100644 --- a/test/vDODO/mintRedeem.test.ts +++ b/test/vDODO/mintRedeem.test.ts @@ -48,30 +48,6 @@ describe("VDODO", () => { describe("vdodo", () => { - it("vdodo init", async () => { - assert.equal( - await ctx.DODO.methods.balanceOf(account0).call(), - decimalStr("1000") - ); - assert.equal( - await ctx.VDODO.methods.balanceOf(account0).call(), - decimalStr("0") - ); - assert.equal( - await ctx.VDODO.methods.alpha().call(), - ctx.alpha - ); - assert.equal( - await ctx.VDODO.methods.lastRewardBlock().call(), - ctx.lastRewardBlock - ); - assert.equal( - await ctx.VDODO.methods.totalSupply().call(), - decimalStr("0") - ); - }); - - it("vdodo-mint-first", async () => { //第一次mint 后 //alpha lastRewardBlock 状态 @@ -106,6 +82,7 @@ describe("VDODO", () => { it("redeem-partial-NotMint", async () => { //多个下级引用 + }); it("redeem-all-haveMint", async () => { @@ -116,6 +93,7 @@ describe("VDODO", () => { //多个下级引用 }); + // it("vdodo first mint with no superior", async () => { // await ctx.VDODO.methods.mint(decimalStr("10"),"0x0000000000000000000000000000000000000000").send(ctx.sendParam(account0)) @@ -145,40 +123,6 @@ describe("VDODO", () => { // ctx.lastRewardBlock // ); // }); - // it("vdodo owner can transfer", async () => { - // await ctx.VDODO.methods.mint(decimalStr("10"),"0x0000000000000000000000000000000000000000").send(ctx.sendParam(account0)) - // assert.equal( - // await ctx.DODO.methods.balanceOf(account0).call(), - // decimalStr("990") - // ); - // assert.equal( - // await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(), - // decimalStr("10") - // ); - // assert.equal( - // await ctx.VDODO.methods.balanceOf(account0).call(), - // decimalStr("0.1") - // ); - // assert.equal( - // await ctx.VDODO.methods.balanceOf(account1).call(), - // decimalStr("0") - // ); - - - // await truffleAssert.reverts( - // ctx.VDODO.methods.transfer(account1,decimalStr("0.1")).send(ctx.sendParam(account0)), - // "vDODOToken: not allowed transfer" - // ) - // assert.equal( - // await ctx.VDODO.methods.balanceOf(account0).call(), - // decimalStr("0.1") - // ); - // assert.equal( - // await ctx.VDODO.methods.balanceOf(account1).call(), - // decimalStr("0") - // ); - - // }); }) }); From ddda681a6a0c93f936132f65cdf071ea90d70bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Tue, 2 Feb 2021 17:24:24 +0800 Subject: [PATCH 29/42] set vdodo address --- contracts/DODOToken/Governance.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/DODOToken/Governance.sol b/contracts/DODOToken/Governance.sol index 9c950d2..ca0cbb5 100644 --- a/contracts/DODOToken/Governance.sol +++ b/contracts/DODOToken/Governance.sol @@ -13,10 +13,10 @@ contract Governance is InitializableOwnable { // ============ Storage ============ - address immutable _DODO_TOKEN_; - - constructor(dodoToken) public { - _DODO_TOKEN_ = dodoToken + address _DODO_TOKEN_; + + function setVDODOAddress(address vodoToken) public onlyOwner{ + _DODO_TOKEN_ = vodoToken; } function getLockedvDODO(address account) external pure returns (uint256 lockedvDODO) { From ce5a86a3d005ed580764b8664e3f73d6e418c65a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Tue, 2 Feb 2021 17:25:45 +0800 Subject: [PATCH 30/42] Update VDODOContext.ts --- test/utils/VDODOContext.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/utils/VDODOContext.ts b/test/utils/VDODOContext.ts index e7ea044..3bd7601 100644 --- a/test/utils/VDODOContext.ts +++ b/test/utils/VDODOContext.ts @@ -90,6 +90,10 @@ export class VDODOContext { ] ) + await this.Governance.methods.setVDODOAddress( + this.VDODO.options.address + ).send(this.sendParam(this.Deployer)) + await this.Governance.methods.initOwner( this.Deployer ).send(this.sendParam(this.Deployer)) From b4eb18e278b2bc8eef3aa428270d0425d340b116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Tue, 2 Feb 2021 17:26:50 +0800 Subject: [PATCH 31/42] update context --- test/utils/VDODOContext.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/utils/VDODOContext.ts b/test/utils/VDODOContext.ts index 3bd7601..814e516 100644 --- a/test/utils/VDODOContext.ts +++ b/test/utils/VDODOContext.ts @@ -90,14 +90,15 @@ export class VDODOContext { ] ) + + await this.Governance.methods.initOwner( + this.Deployer + ).send(this.sendParam(this.Deployer)) + await this.Governance.methods.setVDODOAddress( this.VDODO.options.address ).send(this.sendParam(this.Deployer)) - - await this.Governance.methods.initOwner( - this.Deployer - ).send(this.sendParam(this.Deployer)) - + await this.DODOApprove.methods.init(this.Deployer,this.DODOApproveProxy.options.address).send(this.sendParam(this.Deployer)); await this.DODOApproveProxy.methods.init(this.Deployer, [this.VDODO.options.address]).send(this.sendParam(this.Deployer)); From 0a8247befc491f621942d0346a51d230ba087045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Tue, 2 Feb 2021 17:39:59 +0800 Subject: [PATCH 32/42] transfer close test --- test/vDODO/erc20.test.ts | 72 +++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/test/vDODO/erc20.test.ts b/test/vDODO/erc20.test.ts index 783dd87..39a540d 100644 --- a/test/vDODO/erc20.test.ts +++ b/test/vDODO/erc20.test.ts @@ -10,13 +10,16 @@ import { logGas } from '../utils/Log'; import { VDODOContext, getVDODOContext } from '../utils/VDODOContext'; import { assert } from 'chai'; import BigNumber from 'bignumber.js'; +const truffleAssert = require('truffle-assertions'); let account0: string; let account1: string; +let defaultSuperAddress: string; async function init(ctx: VDODOContext): Promise { account0 = ctx.SpareAccounts[0]; account1 = ctx.SpareAccounts[1]; + defaultSuperAddress = ctx.Maintainer await ctx.mintTestToken(account0, decimalStr("1000")); await ctx.mintTestToken(account1, decimalStr("1000")); @@ -62,44 +65,39 @@ describe("vDODO-erc20", () => { }); it("transfer - close", async () => { + + await ctx.VDODO.methods.mint(decimalStr("10"),defaultSuperAddress).send(ctx.sendParam(account0)) + assert.equal( + await ctx.DODO.methods.balanceOf(account0).call(), + decimalStr("990") + ); + assert.equal( + await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(), + decimalStr("100010") + ); + assert.equal( + await ctx.VDODO.methods.balanceOf(account0).call(), + decimalStr("0.1") + ); + + assert.equal( + await ctx.VDODO.methods.balanceOf(account1).call(), + decimalStr("0") + ); //预期revert + await truffleAssert.reverts( + ctx.VDODO.methods.transfer(account1,decimalStr("0.1")).send(ctx.sendParam(account0)), + "vDODOToken: not allowed transfer" + ) + assert.equal( + await ctx.VDODO.methods.balanceOf(account0).call(), + decimalStr("0.1") + ); + assert.equal( + await ctx.VDODO.methods.balanceOf(account1).call(), + decimalStr("0") + ); + }); - - - // it("vdodo owner can transfer", async () => { - - // await ctx.VDODO.methods.mint(decimalStr("10"),"0x0000000000000000000000000000000000000000").send(ctx.sendParam(account0)) - // assert.equal( - // await ctx.DODO.methods.balanceOf(account0).call(), - // decimalStr("990") - // ); - // assert.equal( - // await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(), - // decimalStr("10") - // ); - // assert.equal( - // await ctx.VDODO.methods.balanceOf(account0).call(), - // decimalStr("0.1") - // ); - // assert.equal( - // await ctx.VDODO.methods.balanceOf(account1).call(), - // decimalStr("0") - // ); - - - // await truffleAssert.reverts( - // ctx.VDODO.methods.transfer(account1,decimalStr("0.1")).send(ctx.sendParam(account0)), - // "vDODOToken: not allowed transfer" - // ) - // assert.equal( - // await ctx.VDODO.methods.balanceOf(account0).call(), - // decimalStr("0.1") - // ); - // assert.equal( - // await ctx.VDODO.methods.balanceOf(account1).call(), - // decimalStr("0") - // ); - - // }); }) }); From f0ec1fa756800fc93c4990c03e3f3d37552049fe Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 2 Feb 2021 18:55:22 +0800 Subject: [PATCH 33/42] ing --- test/vDODO/mintRedeem.test.ts | 11 +++++++++-- truffle-test.sh | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/test/vDODO/mintRedeem.test.ts b/test/vDODO/mintRedeem.test.ts index 24bd0fe..cbbbaa8 100644 --- a/test/vDODO/mintRedeem.test.ts +++ b/test/vDODO/mintRedeem.test.ts @@ -5,8 +5,6 @@ */ -// import * as assert from 'assert'; - import { decimalStr, MAX_UINT256 } from '../utils/Converter'; import { logGas } from '../utils/Log'; import { VDODOContext, getVDODOContext } from '../utils/VDODOContext'; @@ -53,6 +51,15 @@ describe("VDODO", () => { //alpha lastRewardBlock 状态 //user superior info 状态 //vDODO 总量变化,以及vDODO合约dodo余额,user dodo余额 + var b_alpha = await ctx.VDODO.methods.getLatestAlpha().call(); + var b_lastRewardBlock = await ctx.VDODO.methods.lastRewardBlock().call(); + var b_dodo_contract = await ctx.DODO.methods.balanceOf(ctx.DODO.options.address).call(); + var b_dodo_account = await ctx.DODO.methods.balanceOf(account0).call(); + + var userInfo = await ctx.VDODO.methods.userInfo(account0).call(); + + console.log("userInfo:", userInfo) + }); diff --git a/truffle-test.sh b/truffle-test.sh index b3c15f4..3f5940a 100644 --- a/truffle-test.sh +++ b/truffle-test.sh @@ -36,6 +36,12 @@ then truffle test ./test/V2Proxy/proxy.twap.test.ts fi +if [ "$1"x = "vdodo-mintRedeem"x ] +then + truffle test ./test/vDODO/mintRedeem.test.ts +fi + + # if [ "$1"x = "route-incentive"x ] # then # truffle test ./test/Route/Incentive.test.ts From 4e29555f687c2adab999d67029cab6f37a5e1787 Mon Sep 17 00:00:00 2001 From: owen05 Date: Wed, 3 Feb 2021 00:05:45 +0800 Subject: [PATCH 34/42] fix --- contracts/DODOToken/DODOCirculationHelper.sol | 15 +- contracts/DODOToken/vDODOToken.sol | 6 +- test/utils/Converter.ts | 7 + test/utils/VDODOContext.ts | 32 +-- test/vDODO/mintRedeem.test.ts | 197 +++++++++++++----- 5 files changed, 177 insertions(+), 80 deletions(-) diff --git a/contracts/DODOToken/DODOCirculationHelper.sol b/contracts/DODOToken/DODOCirculationHelper.sol index f048713..7cabe22 100644 --- a/contracts/DODOToken/DODOCirculationHelper.sol +++ b/contracts/DODOToken/DODOCirculationHelper.sol @@ -10,7 +10,7 @@ pragma experimental ABIEncoderV2; import {IERC20} from "../intf/IERC20.sol"; import {SafeMath} from "../lib/SafeMath.sol"; import {DecimalMath} from "../lib/DecimalMath.sol"; -import {Ownable} from "../lib/Ownable.sol"; +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; interface IDODOCirculationHelper { // vDODO 锁仓不算流通 @@ -19,28 +19,29 @@ interface IDODOCirculationHelper { function getVDODOWithdrawFeeRatio() external returns (uint256); } -contract DODOCirculationHelper is Ownable { +contract DODOCirculationHelper is InitializableOwnable { using SafeMath for uint256; // ============ Storage ============ + address immutable _VDODO_TOKEN_; address immutable _DODO_TOKEN_; address[] _LOCKED_CONTRACT_ADDRESS_; uint256 public _MIN_PENALTY_RATIO_ = 5 * 10**16; // 5% uint256 public _MAX_PENALTY_RATIO_ = 15 * 10**16; // 15% - constructor(address dodoToken) public { + constructor(address vDodoToken,address dodoToken) public { + _VDODO_TOKEN_ = vDodoToken; _DODO_TOKEN_ = dodoToken; - } // todo + } function addLockedContractAddress(address lockedContract) external onlyOwner {} // todo function removeLockedContractAddress(address lockedContract) external onlyOwner {} // todo function getCirculation() public view returns (uint256 circulation) { - circulation = 10**9; - //TODO circulation 需要乘以decimals吗? + circulation = 10**10 * 10**18; for (uint256 i = 0; i < _LOCKED_CONTRACT_ADDRESS_.length; i++) { circulation -= IERC20(_DODO_TOKEN_).balanceOf(_LOCKED_CONTRACT_ADDRESS_[i]); } @@ -55,7 +56,7 @@ contract DODOCirculationHelper is Ownable { uint256 x = DecimalMath.divCeil( dodoCirculationAmout, - IERC20(_DODO_TOKEN_).balanceOf(address(this))// TODO 这里应该是vdodo的值吧? + IERC20(_VDODO_TOKEN_).totalSupply() ); if (x <= 10**18) { diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index a143596..c546211 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -254,7 +254,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { } function getLatestAlpha() public view returns(uint256) { - uint256 accuDODO = dodoPerBlock * (block.number.sub(lastRewardBlock)); + uint256 accuDODO = dodoPerBlock * (block.number - lastRewardBlock); if (totalSupply > 0) { return uint256(alpha).add(DecimalMath.divFloor(accuDODO, totalSupply)); } else { @@ -299,8 +299,8 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { user.superiorVDODO = uint128(uint256(user.superiorVDODO).add(vdodoAmount)); UserInfo storage superiorUser = userInfo[user.superior]; _mint(superiorUser, vdodoAmount); - uint256 dodoAmount = DecimalMath.mulFloor(vdodoAmount, alpha); - superiorUser.credit = superiorUser.credit.add(DecimalMath.mulFloor(dodoAmount, _SUPERIOR_RATIO_)); + uint256 dodoAmount = DecimalMath.mulCeil(vdodoAmount, alpha); + superiorUser.credit = superiorUser.credit.add(dodoAmount); } } diff --git a/test/utils/Converter.ts b/test/utils/Converter.ts index e01ac5d..d1d67b9 100644 --- a/test/utils/Converter.ts +++ b/test/utils/Converter.ts @@ -1,4 +1,6 @@ import BigNumber from "bignumber.js"; +import { getDefaultWeb3 } from './EVM'; + export const MAX_UINT256 = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" @@ -12,4 +14,9 @@ export function gweiStr(gwei: string): string { export function mweiStr(gwei: string): string { return new BigNumber(gwei).multipliedBy(10 ** 6).toFixed(0, BigNumber.ROUND_DOWN) +} + +export function fromWei(value: string, unit: any): string { + var web3 = getDefaultWeb3(); + return web3.utils.fromWei(value, unit); } \ No newline at end of file diff --git a/test/utils/VDODOContext.ts b/test/utils/VDODOContext.ts index 814e516..c04775c 100644 --- a/test/utils/VDODOContext.ts +++ b/test/utils/VDODOContext.ts @@ -36,8 +36,8 @@ export class VDODOContext { DODOCirculationHelper: Contract; Governance: Contract; - lastRewardBlock:number; - alpha:number; + lastRewardBlock: number; + alpha: number; @@ -57,12 +57,6 @@ export class VDODOContext { ["DODO Token", "DODO", 18] ); - this.DODOCirculationHelper = await contracts.newContract( - contracts.DODO_CULATION_HELPER, - [ - this.DODO.options.address - ] - ); this.DODOApprove = await contracts.newContract( contracts.SMART_APPROVE ); @@ -84,22 +78,29 @@ export class VDODOContext { [ this.Governance.options.address, this.DODO.options.address, - this.DODOCirculationHelper.options.address, + "0x0000000000000000000000000000000000000000", this.DODOApproveProxy.options.address, "VDODO Token", "VDODO" ] ) - + this.DODOCirculationHelper = await contracts.newContract( + contracts.DODO_CULATION_HELPER, + [ + this.VDODO.options.address, + this.DODO.options.address + ] + ); + await this.Governance.methods.initOwner( this.Deployer - ).send(this.sendParam(this.Deployer)) - + ).send(this.sendParam(this.Deployer)) + await this.Governance.methods.setVDODOAddress( this.VDODO.options.address ).send(this.sendParam(this.Deployer)) - - await this.DODOApprove.methods.init(this.Deployer,this.DODOApproveProxy.options.address).send(this.sendParam(this.Deployer)); + + await this.DODOApprove.methods.init(this.Deployer, this.DODOApproveProxy.options.address).send(this.sendParam(this.Deployer)); await this.DODOApproveProxy.methods.init(this.Deployer, [this.VDODO.options.address]).send(this.sendParam(this.Deployer)); @@ -108,11 +109,12 @@ export class VDODOContext { ).send(this.sendParam(this.Deployer)) await this.VDODO.methods.changePerReward(decimalStr("1")).send(this.sendParam(this.Deployer)); + await this.VDODO.methods.updateDODOCirculationHelper(this.DODOCirculationHelper.options.address).send(this.sendParam(this.Deployer)); await this.mintTestToken(this.VDODO.options.address, decimalStr("100000")); this.alpha = await this.VDODO.methods.alpha().call(); this.lastRewardBlock = await this.VDODO.methods.lastRewardBlock().call(); - + console.log(log.blueText("[Init VDODO context]")); console.log("init alpha = " + this.alpha); diff --git a/test/vDODO/mintRedeem.test.ts b/test/vDODO/mintRedeem.test.ts index cbbbaa8..b2fdc18 100644 --- a/test/vDODO/mintRedeem.test.ts +++ b/test/vDODO/mintRedeem.test.ts @@ -5,18 +5,19 @@ */ -import { decimalStr, MAX_UINT256 } from '../utils/Converter'; +import { decimalStr, fromWei } from '../utils/Converter'; import { logGas } from '../utils/Log'; import { VDODOContext, getVDODOContext } from '../utils/VDODOContext'; import { assert } from 'chai'; -import BigNumber from 'bignumber.js'; let account0: string; let account1: string; +let account2: string; async function init(ctx: VDODOContext): Promise { account0 = ctx.SpareAccounts[0]; account1 = ctx.SpareAccounts[1]; + account2 = ctx.SpareAccounts[2]; await ctx.mintTestToken(account0, decimalStr("1000")); await ctx.mintTestToken(account1, decimalStr("1000")); @@ -25,7 +26,40 @@ async function init(ctx: VDODOContext): Promise { await ctx.approveProxy(account1); } -//TODO: 抽象出来mint func +async function getGlobalState(ctx: VDODOContext, logInfo?: string) { + var alpha = await ctx.VDODO.methods.getLatestAlpha().call(); + var lastRewardBlock = await ctx.VDODO.methods.lastRewardBlock().call(); + var totalSuppy = await ctx.VDODO.methods.totalSupply().call(); + console.log(logInfo + " alpha:" + fromWei(alpha, 'ether') + " lastRewardBlock:" + lastRewardBlock + " totalSuppy:" + fromWei(totalSuppy, 'ether')); + return [alpha, lastRewardBlock] +} + +async function dodoBalance(ctx: VDODOContext, user: string, logInfo?: string) { + var dodo_contract = await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(); + var dodo_account = await ctx.DODO.methods.balanceOf(user).call(); + + console.log(logInfo + " DODO:" + fromWei(dodo_contract, 'ether') + " account:" + fromWei(dodo_account, 'ether')); + return [dodo_contract, dodo_account] +} + +async function getUserInfo(ctx: VDODOContext, user: string, logInfo?: string) { + var info = await ctx.VDODO.methods.userInfo(user).call(); + var res = { + "VDODOAmount": info.VDODOAmount, + "superiorVDODO": info.superiorVDODO, + "superior": info.superior, + "credit": info.credit + } + console.log(logInfo + " VDODOAmount:" + fromWei(info.VDODOAmount, 'ether') + " superiorVDODO:" + fromWei(info.superiorVDODO, 'ether') + " superior:" + info.superior + " credit:" + fromWei(info.credit, 'ether')); + return res +} + +async function mint(ctx: VDODOContext, user: string, mintAmount: string, superior: string) { + await ctx.VDODO.methods.mint( + mintAmount, + superior + ).send(ctx.sendParam(user)); +} describe("VDODO", () => { let snapshotId: string; @@ -47,40 +81,124 @@ describe("VDODO", () => { describe("vdodo", () => { it("vdodo-mint-first", async () => { - //第一次mint 后 - //alpha lastRewardBlock 状态 - //user superior info 状态 - //vDODO 总量变化,以及vDODO合约dodo余额,user dodo余额 - var b_alpha = await ctx.VDODO.methods.getLatestAlpha().call(); - var b_lastRewardBlock = await ctx.VDODO.methods.lastRewardBlock().call(); - var b_dodo_contract = await ctx.DODO.methods.balanceOf(ctx.DODO.options.address).call(); - var b_dodo_account = await ctx.DODO.methods.balanceOf(account0).call(); + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account0, "User before"); + await getUserInfo(ctx, account1, "Superior before") + await dodoBalance(ctx, account0, "before") - var userInfo = await ctx.VDODO.methods.userInfo(account0).call(); + await logGas(await ctx.VDODO.methods.mint( + decimalStr("100"), + account1 + ), ctx.sendParam(account0), "mint-fisrt"); - console.log("userInfo:", userInfo) + //增加两个区块 + await ctx.mintTestToken(account0, decimalStr("0")); + await ctx.mintTestToken(account0, decimalStr("0")); + let [alpha,] = await getGlobalState(ctx, "after"); + assert.equal(alpha, "101818181818181818181"); + let userInfo = await getUserInfo(ctx, account0, "User after"); + let superiorInfo = await getUserInfo(ctx, account1, "Superior after") + let [, dodo_u] = await dodoBalance(ctx, account0, "after") + assert.equal(userInfo.VDODOAmount, "1000000000000000000"); + assert.equal(userInfo.superiorVDODO, "100000000000000000"); + assert.equal(userInfo.credit, "0"); + assert.equal(userInfo.superior, account1); + assert.equal(superiorInfo.VDODOAmount, "100000000000000000"); + assert.equal(superiorInfo.superiorVDODO, "0"); + assert.equal(superiorInfo.credit, "10000000000000000000"); + assert.equal(superiorInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(dodo_u, "900000000000000000000") }); it("vdodo-mint-second", async () => { - //第二次mint 后(传入之前的superior地址) - //alpha lastRewardBlock 状态 - //user superior info 状态 - //vDODO 总量变化,以及vDODO合约dodo余额,user dodo余额 + await mint(ctx, account0, decimalStr("100"), account1) + + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account0, "User before"); + await getUserInfo(ctx, account1, "Superior before") + await dodoBalance(ctx, account0, "before") + + await logGas(await ctx.VDODO.methods.mint( + decimalStr("100"), + account1 + ), ctx.sendParam(account0), "mint-second"); + + //增加一个区块 + await ctx.mintTestToken(account0, decimalStr("0")); + + let [alpha,] = await getGlobalState(ctx, "after"); + assert.equal(alpha, "101365693130399012751"); + let userInfo = await getUserInfo(ctx, account0, "User after"); + let superiorInfo = await getUserInfo(ctx, account1, "Superior after") + let [, dodo_u] = await dodoBalance(ctx, account0, "after") + assert.equal(userInfo.VDODOAmount, "1990990990990990990"); + assert.equal(userInfo.superiorVDODO, "199099099099099099"); + assert.equal(userInfo.credit, "0"); + assert.equal(userInfo.superior, account1); + + assert.equal(superiorInfo.VDODOAmount, "199099099099099099"); + assert.equal(superiorInfo.superiorVDODO, "0"); + assert.equal(superiorInfo.credit, "19999999999999999990"); + assert.equal(superiorInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(dodo_u, "800000000000000000000") }); it("vdodo-mint-second-otherSuperior", async () => { - //第二次mint 后(传入非之前superior地址) - //alpha lastRewardBlock 状态 - //user superior info 状态 - //vDODO 总量变化,以及vDODO合约dodo余额,user dodo余额 + await mint(ctx, account0, decimalStr("100"), account1) + + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account0, "User before"); + await getUserInfo(ctx, account1, "Superior before") + await dodoBalance(ctx, account0, "before") + + await logGas(await ctx.VDODO.methods.mint( + decimalStr("100"), + account2 + ), ctx.sendParam(account0), "mint-second"); + + //增加一个区块 + await ctx.mintTestToken(account0, decimalStr("0")); + + let [alpha,] = await getGlobalState(ctx, "after"); + assert.equal(alpha, "101365693130399012751"); + let userInfo = await getUserInfo(ctx, account0, "User after"); + let superiorInfo = await getUserInfo(ctx, account1, "Superior after") + let [, dodo_u] = await dodoBalance(ctx, account0, "after") + assert.equal(userInfo.VDODOAmount, "1990990990990990990"); + assert.equal(userInfo.superiorVDODO, "199099099099099099"); + assert.equal(userInfo.credit, "0"); + assert.equal(userInfo.superior, account1); + + assert.equal(superiorInfo.VDODOAmount, "199099099099099099"); + assert.equal(superiorInfo.superiorVDODO, "0"); + assert.equal(superiorInfo.credit, "19999999999999999990"); + assert.equal(superiorInfo.superior, "0x0000000000000000000000000000000000000000"); + + let otherInfo = await getUserInfo(ctx, account2, "Superior after") + + assert.equal(otherInfo.VDODOAmount, "0"); + assert.equal(otherInfo.superiorVDODO, "0"); + assert.equal(otherInfo.credit, "0"); + assert.equal(otherInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(dodo_u, "800000000000000000000") }); - it("redeem-amount-read", async () => { - //正确读取 withdrawAmount 字段 + it.only("redeem-amount-read", async () => { + await mint(ctx, account0, decimalStr("100"), account1) + + let [dodoReceive, burnDodoAmount, withdrawFeeDodoAmount] = await ctx.VDODO.methods.getWithdrawAmount(decimalStr("1")).call(); + + console.log("dodoReceive:", dodoReceive) + console.log("burnDodoAmount:", burnDodoAmount) + console.log("withdrawFeeDodoAmount:", withdrawFeeDodoAmount) + }); it("redeem-partial-haveMint", async () => { @@ -89,7 +207,7 @@ describe("VDODO", () => { it("redeem-partial-NotMint", async () => { //多个下级引用 - + }); it("redeem-all-haveMint", async () => { @@ -100,36 +218,5 @@ describe("VDODO", () => { //多个下级引用 }); - - // it("vdodo first mint with no superior", async () => { - - // await ctx.VDODO.methods.mint(decimalStr("10"),"0x0000000000000000000000000000000000000000").send(ctx.sendParam(account0)) - // assert.equal( - // await ctx.DODO.methods.balanceOf(account0).call(), - // decimalStr("990") - // ); - // assert.equal( - // await await ctx.VDODO.methods.alpha().call(), - // await ctx.alpha - // ); - // assert.equal( - // await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(), - // decimalStr("10") - // ); - // assert.equal( - // await ctx.VDODO.methods.balanceOf(account0).call(), - // decimalStr("0.1") - // ); - - // assert.equal( - // await ctx.VDODO.methods.totalSupply().call(), - // decimalStr("0.1") - // ); - // assert.notEqual( - // await ctx.VDODO.methods.lastRewardBlock().call(), - // ctx.lastRewardBlock - // ); - // }); - }) }); From d90c6ab2b2593344a8e2775cb807abcaa0eaf4c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Wed, 3 Feb 2021 00:13:45 +0800 Subject: [PATCH 35/42] update modifier --- contracts/DODOToken/vDODOToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index c546211..ec4e526 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -333,7 +333,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { address from, address to, uint256 _amount - ) internal balanceEnough(msg.sender, _amount) canTransfer { + ) internal balanceEnough(from, _amount) canTransfer { require(from != address(0), "transfer from the zero address"); require(to != address(0), "transfer to the zero address"); From deabb55ef208684913645f075b60d9a0c16b32f0 Mon Sep 17 00:00:00 2001 From: mingda Date: Wed, 3 Feb 2021 12:15:13 +0800 Subject: [PATCH 36/42] DODO Migration to BSC --- contracts/DODOToken/DODOMigrationBSC.sol | 50 ++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 contracts/DODOToken/DODOMigrationBSC.sol diff --git a/contracts/DODOToken/DODOMigrationBSC.sol b/contracts/DODOToken/DODOMigrationBSC.sol new file mode 100644 index 0000000..382946a --- /dev/null +++ b/contracts/DODOToken/DODOMigrationBSC.sol @@ -0,0 +1,50 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {IERC20} from "../intf/IERC20.sol"; +import {SafeMath} from "../lib/SafeMath.sol"; +import {Ownable} from "../lib/Ownable.sol"; + +contract DODOMigrationBSC is Ownable { + using SafeMath for uint256; + + // ============ Storage ============ + + address immutable _ETH_DODO_TOKEN_ = 0x43Dfc4159D86F3A37A5A4B3D4580b888ad7d4DDd; + address immutable _BSC_DODO_TOKEN_ = 0xa6E37b1d3690C8E608Bb11AFE193fA7C88141643; + mapping(address => uint256) internal balances; + + bool public _IS_ETH_NETWORK_; + + constructor(bool isETHNetwork) public { + _IS_ETH_NETWORK_ = isETHNetwork; + } + + // ============ Events ============ + + event Lock(address indexed sender, address indexed mintTo, uint256 amount); + event Unlock(address indexed to, uint256 amount); + + // ============ Functions ============ + + function lock(uint256 amount, address mintTo) external { + address dodoToken = _IS_ETH_NETWORK_ ? _ETH_DODO_TOKEN_ : _BSC_DODO_TOKEN_; + IERC20(dodoToken).transferFrom(msg.sender, address(this), amount); + balances[msg.sender] = balances[msg.sender].add(amount); + emit Lock(msg.sender, mintTo, amount); + } + + function unlock(address unlockTo, uint256 amount) external onlyOwner { + address dodoToken = _IS_ETH_NETWORK_ ? _ETH_DODO_TOKEN_ : _BSC_DODO_TOKEN_; + require(balances[unlockTo] >= amount); + balances[unlockTo] = balances[unlockTo].sub(amount); + IERC20(dodoToken).transfer(unlockTo, amount); + emit Unlock(unlockTo, amount); + } +} From 23bfca05dff844b5d73919b4320bc6247dfad007 Mon Sep 17 00:00:00 2001 From: mingda Date: Wed, 3 Feb 2021 12:18:17 +0800 Subject: [PATCH 37/42] change _IS_ETH_NETWORK_ to immutable --- contracts/DODOToken/DODOMigrationBSC.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/DODOToken/DODOMigrationBSC.sol b/contracts/DODOToken/DODOMigrationBSC.sol index 382946a..b0635e5 100644 --- a/contracts/DODOToken/DODOMigrationBSC.sol +++ b/contracts/DODOToken/DODOMigrationBSC.sol @@ -20,7 +20,7 @@ contract DODOMigrationBSC is Ownable { address immutable _BSC_DODO_TOKEN_ = 0xa6E37b1d3690C8E608Bb11AFE193fA7C88141643; mapping(address => uint256) internal balances; - bool public _IS_ETH_NETWORK_; + bool immutable _IS_ETH_NETWORK_; constructor(bool isETHNetwork) public { _IS_ETH_NETWORK_ = isETHNetwork; From ef9e7f5dcce8c6c450ca3035dcc74ea4a6719e5a Mon Sep 17 00:00:00 2001 From: owen05 Date: Wed, 3 Feb 2021 13:24:52 +0800 Subject: [PATCH 38/42] vdodo-mintredeem test over --- .../SmartRoute/helper/DODOCalleeHelper.sol | 4 +- test/vDODO/mintRedeem.test.ts | 158 ++++++++++++++++-- 2 files changed, 148 insertions(+), 14 deletions(-) diff --git a/contracts/SmartRoute/helper/DODOCalleeHelper.sol b/contracts/SmartRoute/helper/DODOCalleeHelper.sol index a77f606..5ea225f 100644 --- a/contracts/SmartRoute/helper/DODOCalleeHelper.sol +++ b/contracts/SmartRoute/helper/DODOCalleeHelper.sol @@ -76,7 +76,9 @@ contract DODOCalleeHelper is ReentrancyGuard { to.transfer(amount); } } else { - SafeERC20.safeTransfer(IERC20(token), to, amount); + if (amount > 0) { + SafeERC20.safeTransfer(IERC20(token), to, amount); + } } } } diff --git a/test/vDODO/mintRedeem.test.ts b/test/vDODO/mintRedeem.test.ts index b2fdc18..1da9efc 100644 --- a/test/vDODO/mintRedeem.test.ts +++ b/test/vDODO/mintRedeem.test.ts @@ -13,17 +13,27 @@ import { assert } from 'chai'; let account0: string; let account1: string; let account2: string; +let account3: string; +let account4: string; async function init(ctx: VDODOContext): Promise { account0 = ctx.SpareAccounts[0]; account1 = ctx.SpareAccounts[1]; account2 = ctx.SpareAccounts[2]; + account3 = ctx.SpareAccounts[3]; + account4 = ctx.SpareAccounts[4]; - await ctx.mintTestToken(account0, decimalStr("1000")); + await ctx.mintTestToken(account0, decimalStr("100000")); await ctx.mintTestToken(account1, decimalStr("1000")); + await ctx.mintTestToken(account2, decimalStr("1000")); + await ctx.mintTestToken(account3, decimalStr("1000")); + await ctx.mintTestToken(account4, decimalStr("1000")); await ctx.approveProxy(account0); await ctx.approveProxy(account1); + await ctx.approveProxy(account2); + await ctx.approveProxy(account3); + await ctx.approveProxy(account4); } async function getGlobalState(ctx: VDODOContext, logInfo?: string) { @@ -96,10 +106,11 @@ describe("VDODO", () => { await ctx.mintTestToken(account0, decimalStr("0")); let [alpha,] = await getGlobalState(ctx, "after"); - assert.equal(alpha, "101818181818181818181"); let userInfo = await getUserInfo(ctx, account0, "User after"); let superiorInfo = await getUserInfo(ctx, account1, "Superior after") let [, dodo_u] = await dodoBalance(ctx, account0, "after") + + assert.equal(alpha, "101818181818181818181"); assert.equal(userInfo.VDODOAmount, "1000000000000000000"); assert.equal(userInfo.superiorVDODO, "100000000000000000"); assert.equal(userInfo.credit, "0"); @@ -110,7 +121,7 @@ describe("VDODO", () => { assert.equal(superiorInfo.credit, "10000000000000000000"); assert.equal(superiorInfo.superior, "0x0000000000000000000000000000000000000000"); - assert.equal(dodo_u, "900000000000000000000") + assert.equal(dodo_u, "99900000000000000000000") }); it("vdodo-mint-second", async () => { @@ -130,10 +141,11 @@ describe("VDODO", () => { await ctx.mintTestToken(account0, decimalStr("0")); let [alpha,] = await getGlobalState(ctx, "after"); - assert.equal(alpha, "101365693130399012751"); let userInfo = await getUserInfo(ctx, account0, "User after"); let superiorInfo = await getUserInfo(ctx, account1, "Superior after") let [, dodo_u] = await dodoBalance(ctx, account0, "after") + + assert.equal(alpha, "101365693130399012751"); assert.equal(userInfo.VDODOAmount, "1990990990990990990"); assert.equal(userInfo.superiorVDODO, "199099099099099099"); assert.equal(userInfo.credit, "0"); @@ -144,7 +156,7 @@ describe("VDODO", () => { assert.equal(superiorInfo.credit, "19999999999999999990"); assert.equal(superiorInfo.superior, "0x0000000000000000000000000000000000000000"); - assert.equal(dodo_u, "800000000000000000000") + assert.equal(dodo_u, "99800000000000000000000") }); @@ -165,10 +177,11 @@ describe("VDODO", () => { await ctx.mintTestToken(account0, decimalStr("0")); let [alpha,] = await getGlobalState(ctx, "after"); - assert.equal(alpha, "101365693130399012751"); let userInfo = await getUserInfo(ctx, account0, "User after"); let superiorInfo = await getUserInfo(ctx, account1, "Superior after") let [, dodo_u] = await dodoBalance(ctx, account0, "after") + + assert.equal(alpha, "101365693130399012751"); assert.equal(userInfo.VDODOAmount, "1990990990990990990"); assert.equal(userInfo.superiorVDODO, "199099099099099099"); assert.equal(userInfo.credit, "0"); @@ -186,37 +199,156 @@ describe("VDODO", () => { assert.equal(otherInfo.credit, "0"); assert.equal(otherInfo.superior, "0x0000000000000000000000000000000000000000"); - assert.equal(dodo_u, "800000000000000000000") + assert.equal(dodo_u, "99800000000000000000000") }); - it.only("redeem-amount-read", async () => { + it("redeem-amount-read", async () => { await mint(ctx, account0, decimalStr("100"), account1) let [dodoReceive, burnDodoAmount, withdrawFeeDodoAmount] = await ctx.VDODO.methods.getWithdrawAmount(decimalStr("1")).call(); - console.log("dodoReceive:", dodoReceive) - console.log("burnDodoAmount:", burnDodoAmount) - console.log("withdrawFeeDodoAmount:", withdrawFeeDodoAmount) - + assert.equal(dodoReceive, decimalStr("85")); + assert.equal(burnDodoAmount, decimalStr("0")); + assert.equal(withdrawFeeDodoAmount, decimalStr("15")); }); + it("redeem-partial-haveMint", async () => { + await mint(ctx, account0, decimalStr("10000"), account1) + + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account0, "User before"); + await getUserInfo(ctx, account1, "Superior before") + await dodoBalance(ctx, account0, "before") + + await logGas(await ctx.VDODO.methods.redeem(decimalStr("10")), ctx.sendParam(account0), "redeem-partial-haveMint"); + + let [alpha,] = await getGlobalState(ctx, "after"); + let userInfo = await getUserInfo(ctx, account0, "User after"); + let superiorInfo = await getUserInfo(ctx, account1, "Superior after") + let [, dodo_u] = await dodoBalance(ctx, account0, "after") + + assert.equal(alpha, "101524380165289256197"); + assert.equal(userInfo.VDODOAmount, "90000000000000000000"); + assert.equal(userInfo.superiorVDODO, "9000000000000000000"); + assert.equal(userInfo.credit, "0"); + assert.equal(userInfo.superior, account1); + + assert.equal(superiorInfo.VDODOAmount, "9000000000000000000"); + assert.equal(superiorInfo.superiorVDODO, "0"); + assert.equal(superiorInfo.credit, "899990909090909090910"); + assert.equal(superiorInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(dodo_u, "90850077272727272727265") }); + it("redeem-partial-NotMint", async () => { //多个下级引用 + await mint(ctx, account1, decimalStr("100"), account0) + await mint(ctx, account2, decimalStr("100"), account0) + await mint(ctx, account3, decimalStr("100"), account0) + await mint(ctx, account4, decimalStr("100"), account0) + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account0, "User before"); + await getUserInfo(ctx, account3, "One of referer before"); + await dodoBalance(ctx, account0, "before") + + let account0VdodoAmount = await ctx.VDODO.methods.balanceOf(account0).call() + + await logGas(await ctx.VDODO.methods.redeem((account0VdodoAmount - 3000) + ""), ctx.sendParam(account0), "redeem-partial-NotMint"); + + let [alpha,] = await getGlobalState(ctx, "after"); + let userInfo = await getUserInfo(ctx, account0, "User after"); + let superiorInfo = await getUserInfo(ctx, account3, "One of referer after") + let [, dodo_u] = await dodoBalance(ctx, account0, "after") + + assert.equal(alpha, "101909933011338172201"); + assert.equal(userInfo.VDODOAmount, "393425809544634067"); + assert.equal(userInfo.superiorVDODO, "0"); + assert.equal(userInfo.credit, "39999999999999999876"); + assert.equal(userInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(superiorInfo.VDODOAmount, "986527067608148689"); + assert.equal(superiorInfo.superiorVDODO, "98652706760814868"); + assert.equal(superiorInfo.credit, "0"); + assert.equal(superiorInfo.superior, account0); + + assert.equal(dodo_u, "100000232341473424735076") }); + it("redeem-all-haveMint", async () => { + //第一笔mint不动,防止totalSupply过小 + await mint(ctx, account0, decimalStr("10000"), account1) + await mint(ctx, account1, decimalStr("100"), account2) + + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account1, "User before"); + await getUserInfo(ctx, account2, "Superior before") + await dodoBalance(ctx, account1, "before") + + let account1VdodoAmount = await ctx.VDODO.methods.balanceOf(account1).call() + + await logGas(await ctx.VDODO.methods.redeem(account1VdodoAmount), ctx.sendParam(account1), "redeem-all-haveMint"); + + let [alpha,] = await getGlobalState(ctx, "after"); + let userInfo = await getUserInfo(ctx, account1, "User after"); + let superiorInfo = await getUserInfo(ctx, account2, "Superior after") + let [, dodo_u] = await dodoBalance(ctx, account1, "after") + + assert.equal(alpha, "100154592821433302856"); + assert.equal(userInfo.VDODOAmount, "9999090991728024725"); + assert.equal(userInfo.superiorVDODO, "0"); + assert.equal(userInfo.credit, "1000000000000000000000"); + assert.equal(userInfo.superior, account2); + + assert.equal(superiorInfo.VDODOAmount, "8998462015594"); + assert.equal(superiorInfo.superiorVDODO, "0"); + assert.equal(superiorInfo.credit, "0"); + assert.equal(superiorInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(dodo_u, "985084929758388492933") }); + it("redeem-all-NoMint", async () => { //多个下级引用 - }); + await mint(ctx, account1, decimalStr("100"), account0) + await mint(ctx, account2, decimalStr("100"), account0) + await mint(ctx, account3, decimalStr("100"), account0) + await mint(ctx, account4, decimalStr("100"), account0) + await getGlobalState(ctx, "before"); + await getUserInfo(ctx, account0, "User before"); + await getUserInfo(ctx, account3, "One of referer before"); + await dodoBalance(ctx, account0, "before") + + let account0VdodoAmount = await ctx.VDODO.methods.balanceOf(account0).call() + + await logGas(await ctx.VDODO.methods.redeem(account0VdodoAmount), ctx.sendParam(account0), "redeem-all-NotMint"); + + let [alpha,] = await getGlobalState(ctx, "after"); + let userInfo = await getUserInfo(ctx, account0, "User after"); + let superiorInfo = await getUserInfo(ctx, account3, "One of referer after") + let [, dodo_u] = await dodoBalance(ctx, account0, "after") + + assert.equal(alpha, "101909933011338182738"); + assert.equal(userInfo.VDODOAmount, "393425809544631067"); + assert.equal(userInfo.superiorVDODO, "0"); + assert.equal(userInfo.credit, "39999999999999999876"); + assert.equal(userInfo.superior, "0x0000000000000000000000000000000000000000"); + + assert.equal(superiorInfo.VDODOAmount, "986527067608148689"); + assert.equal(superiorInfo.superiorVDODO, "98652706760814868"); + assert.equal(superiorInfo.credit, "0"); + assert.equal(superiorInfo.superior, account0); + + assert.equal(dodo_u, "100000232341473424994923") + }); }) }); From 139b0edef8007af27fbfc5a80ebe7c9b1df056ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=96=B0=E5=88=9A?= <719802264@qq.com> Date: Wed, 3 Feb 2021 15:28:49 +0800 Subject: [PATCH 39/42] update test case --- contracts/DODOToken/DODOCirculationHelper.sol | 11 +- test/vDODO/erc20.test.ts | 249 +++++++++++++++++- test/vDODO/global.test.ts | 74 +++++- 3 files changed, 322 insertions(+), 12 deletions(-) diff --git a/contracts/DODOToken/DODOCirculationHelper.sol b/contracts/DODOToken/DODOCirculationHelper.sol index 7cabe22..daac59d 100644 --- a/contracts/DODOToken/DODOCirculationHelper.sol +++ b/contracts/DODOToken/DODOCirculationHelper.sol @@ -58,13 +58,16 @@ contract DODOCirculationHelper is InitializableOwnable { dodoCirculationAmout, IERC20(_VDODO_TOKEN_).totalSupply() ); - - if (x <= 10**18) { + + ratio = geRatioValue(x); + } + function geRatioValue(uint256 input) public view returns (uint256 ratio) { + if (input <= 10**18) { return _MIN_PENALTY_RATIO_; - } else if (x >= 10**19) { + } else if (input >= 10**19) { return _MAX_PENALTY_RATIO_; } else { - uint256 xTemp = x.sub(DecimalMath.ONE).div(9); + uint256 xTemp = input.sub(DecimalMath.ONE).div(9); uint256 premium = DecimalMath.ONE2.sub(xTemp.mul(xTemp)).sqrt(); ratio = _MAX_PENALTY_RATIO_ - diff --git a/test/vDODO/erc20.test.ts b/test/vDODO/erc20.test.ts index 39a540d..8936e6a 100644 --- a/test/vDODO/erc20.test.ts +++ b/test/vDODO/erc20.test.ts @@ -5,7 +5,7 @@ */ -import { decimalStr, MAX_UINT256 } from '../utils/Converter'; +import { decimalStr, fromWei } from '../utils/Converter'; import { logGas } from '../utils/Log'; import { VDODOContext, getVDODOContext } from '../utils/VDODOContext'; import { assert } from 'chai'; @@ -14,20 +14,65 @@ const truffleAssert = require('truffle-assertions'); let account0: string; let account1: string; +let account2: string; +let account3: string; let defaultSuperAddress: string; +let owner: string; async function init(ctx: VDODOContext): Promise { account0 = ctx.SpareAccounts[0]; account1 = ctx.SpareAccounts[1]; + account2 = ctx.SpareAccounts[2]; + account3 = ctx.SpareAccounts[3]; defaultSuperAddress = ctx.Maintainer + owner = ctx.Deployer await ctx.mintTestToken(account0, decimalStr("1000")); - await ctx.mintTestToken(account1, decimalStr("1000")); + await ctx.mintTestToken(account2, decimalStr("1000")); await ctx.approveProxy(account0); await ctx.approveProxy(account1); + await ctx.approveProxy(account2); + await ctx.approveProxy(account3); + + await ctx.VDODO.methods.setCantransfer(true).send(ctx.sendParam(owner)) } +async function getGlobalState(ctx: VDODOContext, logInfo?: string) { + var alpha = await ctx.VDODO.methods.getLatestAlpha().call(); + var lastRewardBlock = await ctx.VDODO.methods.lastRewardBlock().call(); + var totalSuppy = await ctx.VDODO.methods.totalSupply().call(); + // console.log(logInfo + " alpha:" + fromWei(alpha, 'ether') + " lastRewardBlock:" + lastRewardBlock + " totalSuppy:" + fromWei(totalSuppy, 'ether')); + return [alpha, lastRewardBlock,totalSuppy] + } + + async function dodoBalance(ctx: VDODOContext, user: string, logInfo?: string) { + var dodo_contract = await ctx.DODO.methods.balanceOf(ctx.VDODO.options.address).call(); + var dodo_account = await ctx.DODO.methods.balanceOf(user).call(); + + // console.log(logInfo + " DODO:" + fromWei(dodo_contract, 'ether') + " account:" + fromWei(dodo_account, 'ether')); + return [dodo_contract, dodo_account] + } + + async function getUserInfo(ctx: VDODOContext, user: string, logInfo?: string) { + var info = await ctx.VDODO.methods.userInfo(user).call(); + var res = { + "VDODOAmount": info.VDODOAmount, + "superiorVDODO": info.superiorVDODO, + "superior": info.superior, + "credit": info.credit + } + // console.log(logInfo + " VDODOAmount:" + fromWei(info.VDODOAmount, 'ether') + " superiorVDODO:" + fromWei(info.superiorVDODO, 'ether') + " superior:" + info.superior + " credit:" + fromWei(info.credit, 'ether')); + return res + } + + async function mint(ctx: VDODOContext, user: string, mintAmount: string, superior: string) { + await ctx.VDODO.methods.mint( + mintAmount, + superior + ).send(ctx.sendParam(user)); + } + describe("vDODO-erc20", () => { let snapshotId: string; let ctx: VDODOContext; @@ -52,6 +97,100 @@ describe("vDODO-erc20", () => { //检查四个人 【包括from, to 以及各自的上级】,info变化 //alpha lastRewardBlock //各自dodo余额变化 + + let [,lastRewardBlockStart,] = await getGlobalState(ctx, "before"); + await ctx.VDODO.methods.mint(decimalStr("10"),account1).send(ctx.sendParam(account0)) + await ctx.VDODO.methods.mint(decimalStr("10"),account3).send(ctx.sendParam(account2)) + + //增加一个区块 + await ctx.mintTestToken(account0, decimalStr("0")); + let [alpha,lastRewardBlock,] = await getGlobalState(ctx, "after"); + + assert.equal(lastRewardBlock,Number(lastRewardBlockStart)+11); + + assert.equal(alpha, "113833992094861660108"); + var totalSuppy = await ctx.VDODO.methods.totalSupply().call(); + assert.equal( + totalSuppy + , decimalStr("0.210833333333333332")); + + + + let userInfo0 = await getUserInfo(ctx, account0, "User0 "); + assert.equal(userInfo0.VDODOAmount, decimalStr("0.1")); + assert.equal(userInfo0.superiorVDODO, decimalStr("0.01")); + assert.equal(userInfo0.credit, "0"); + let userInfo1 = await getUserInfo(ctx, account1, "User0 Superior ") + assert.equal(userInfo1.VDODOAmount, decimalStr("0.01")); + assert.equal(userInfo1.superiorVDODO, decimalStr("0")); + assert.equal(userInfo1.credit, decimalStr("1")); + + let userInfo2 = await getUserInfo(ctx, account2, "User2 "); + assert.equal(userInfo2.VDODOAmount, decimalStr("0.091666666666666666")); + assert.equal(userInfo2.superiorVDODO, decimalStr("0.009166666666666666")); + assert.equal(userInfo2.credit, decimalStr("0")); + let userInfo3 = await getUserInfo(ctx, account3, "User2 Superior"); + assert.equal(userInfo3.VDODOAmount, decimalStr("0.009166666666666666")); + assert.equal(userInfo3.superiorVDODO, decimalStr("0")); + assert.equal(userInfo3.credit, decimalStr("0.999999999999999928")); + + + let [, dodo_u0] = await dodoBalance(ctx, account0, "start") + assert.equal(dodo_u0, "990000000000000000000"); + let [, dodo_u1] = await dodoBalance(ctx, account1, "start") + assert.equal(dodo_u1, "0"); + let [, dodo_u2] = await dodoBalance(ctx, account2, "start") + assert.equal(dodo_u2, "990000000000000000000"); + let [, dodo_u3] = await dodoBalance(ctx, account3, "start") + assert.equal(dodo_u3, "0"); + + await logGas(await ctx.VDODO.methods.transfer( + account2, + decimalStr("0.1") + ), ctx.sendParam(account0), "transfer"); + + + // await ctx.VDODO.methods.transfer(account2,decimalStr("0.1")).send(ctx.sendParam(account0)) + + let userInfo0_after = await getUserInfo(ctx, account0, "userInfo0_after"); + let userInfo1_after = await getUserInfo(ctx, account1, "userInfo1_after"); + let userInfo2_after = await getUserInfo(ctx, account2, "userInfo2_after"); + let userInfo3_after = await getUserInfo(ctx, account3, "userInfo3_after"); + + + assert.equal(userInfo0_after.VDODOAmount, "0"); + assert.equal(userInfo0_after.superiorVDODO, "0"); + assert.equal(userInfo0_after.credit, "0"); + + assert.equal(userInfo1_after.VDODOAmount, decimalStr("0.001566666666666667")); + assert.equal(userInfo1_after.superiorVDODO, decimalStr("0")); + assert.equal(userInfo1_after.credit, "0"); + + assert.equal(userInfo2_after.VDODOAmount, decimalStr("0.191666666666666666")); + assert.equal(userInfo2_after.superiorVDODO, decimalStr("0.019166666666666666")); + assert.equal(userInfo2_after.credit, "0"); + + assert.equal(userInfo3_after.VDODOAmount, decimalStr("0.019166666666666666")); + assert.equal(userInfo3_after.superiorVDODO, decimalStr("0")); + assert.equal(userInfo3_after.credit, decimalStr("2.185770750988142222")); + + + + let [alphaEnd,lastRewardBlockEnd,totalSuppyEnd] = await getGlobalState(ctx, "end"); + assert.equal(alphaEnd, decimalStr("118.577075098814229308")); + assert.equal(totalSuppyEnd, decimalStr("0.212399999999999999")); + assert.equal(lastRewardBlockEnd,Number(lastRewardBlock)+2); + + + let [, dodo_u0_end] = await dodoBalance(ctx, account0, "end") + assert.equal(dodo_u0_end, "990000000000000000000"); + let [, dodo_u1_end] = await dodoBalance(ctx, account1, "end") + assert.equal(dodo_u1_end, "0"); + let [, dodo_u2_end] = await dodoBalance(ctx, account2, "end") + assert.equal(dodo_u2_end, "990000000000000000000"); + let [, dodo_u3_end] = await dodoBalance(ctx, account3, "end") + assert.equal(dodo_u3_end, "0"); + }); it("transferFrom-vdodo", async () => { @@ -60,14 +199,118 @@ describe("vDODO-erc20", () => { //各自dodo余额变化 //approve 状态变化 + let [,lastRewardBlockStart,] = await getGlobalState(ctx, "before"); + await ctx.VDODO.methods.mint(decimalStr("10"),account1).send(ctx.sendParam(account0)) + await ctx.VDODO.methods.mint(decimalStr("10"),account3).send(ctx.sendParam(account2)) + + //增加一个区块 + await ctx.mintTestToken(account0, decimalStr("0")); + let [alpha,lastRewardBlock,] = await getGlobalState(ctx, "after"); + + assert.equal(lastRewardBlock,Number(lastRewardBlockStart)+11); + + assert.equal(alpha, "113833992094861660108"); + var totalSuppy = await ctx.VDODO.methods.totalSupply().call(); + assert.equal( + totalSuppy + , decimalStr("0.210833333333333332")); + + + + let userInfo0 = await getUserInfo(ctx, account0, "User0 "); + assert.equal(userInfo0.VDODOAmount, decimalStr("0.1")); + assert.equal(userInfo0.superiorVDODO, decimalStr("0.01")); + assert.equal(userInfo0.credit, "0"); + let userInfo1 = await getUserInfo(ctx, account1, "User0 Superior ") + assert.equal(userInfo1.VDODOAmount, decimalStr("0.01")); + assert.equal(userInfo1.superiorVDODO, decimalStr("0")); + assert.equal(userInfo1.credit, decimalStr("1")); + + let userInfo2 = await getUserInfo(ctx, account2, "User2 "); + assert.equal(userInfo2.VDODOAmount, decimalStr("0.091666666666666666")); + assert.equal(userInfo2.superiorVDODO, decimalStr("0.009166666666666666")); + assert.equal(userInfo2.credit, decimalStr("0")); + let userInfo3 = await getUserInfo(ctx, account3, "User2 Superior"); + assert.equal(userInfo3.VDODOAmount, decimalStr("0.009166666666666666")); + assert.equal(userInfo3.superiorVDODO, decimalStr("0")); + assert.equal(userInfo3.credit, decimalStr("0.999999999999999928")); + + + let [, dodo_u0] = await dodoBalance(ctx, account0, "start") + assert.equal(dodo_u0, "990000000000000000000"); + let [, dodo_u1] = await dodoBalance(ctx, account1, "start") + assert.equal(dodo_u1, "0"); + let [, dodo_u2] = await dodoBalance(ctx, account2, "start") + assert.equal(dodo_u2, "990000000000000000000"); + let [, dodo_u3] = await dodoBalance(ctx, account3, "start") + assert.equal(dodo_u3, "0"); + + + await logGas(await ctx.VDODO.methods.approve( + account3, + decimalStr("0.1") + ), ctx.sendParam(account0), "approve"); + + await logGas(await ctx.VDODO.methods.transferFrom( + account0, + account2, + decimalStr("0.1") + ), ctx.sendParam(account3), "transferFrom"); + + let userInfo0_after = await getUserInfo(ctx, account0, "userInfo0_after"); + let userInfo1_after = await getUserInfo(ctx, account1, "userInfo1_after"); + let userInfo2_after = await getUserInfo(ctx, account2, "userInfo2_after"); + let userInfo3_after = await getUserInfo(ctx, account3, "userInfo3_after"); + + + assert.equal(userInfo0_after.VDODOAmount, "0"); + assert.equal(userInfo0_after.superiorVDODO, "0"); + assert.equal(userInfo0_after.credit, "0"); + + assert.equal(userInfo1_after.VDODOAmount, decimalStr("0.001891025641025642")); + assert.equal(userInfo1_after.superiorVDODO, decimalStr("0")); + assert.equal(userInfo1_after.credit, "0"); + + assert.equal(userInfo2_after.VDODOAmount, decimalStr("0.191666666666666666")); + assert.equal(userInfo2_after.superiorVDODO, decimalStr("0.019166666666666666")); + assert.equal(userInfo2_after.credit, "0"); + + assert.equal(userInfo3_after.VDODOAmount, decimalStr("0.019166666666666666")); + assert.equal(userInfo3_after.superiorVDODO, decimalStr("0")); + assert.equal(userInfo3_after.credit, decimalStr("2.233201581027667914")); + + + + let [alphaEnd,lastRewardBlockEnd,totalSuppyEnd] = await getGlobalState(ctx, "end"); + assert.equal(alphaEnd, decimalStr("123.320158102766798508")); + assert.equal(totalSuppyEnd, decimalStr("0.212724358974358974")); + assert.equal(lastRewardBlockEnd,Number(lastRewardBlock)+3); + + + let [, dodo_u0_end] = await dodoBalance(ctx, account0, "end") + assert.equal(dodo_u0_end, "990000000000000000000"); + let [, dodo_u1_end] = await dodoBalance(ctx, account1, "end") + assert.equal(dodo_u1_end, "0"); + let [, dodo_u2_end] = await dodoBalance(ctx, account2, "end") + assert.equal(dodo_u2_end, "990000000000000000000"); + let [, dodo_u3_end] = await dodoBalance(ctx, account3, "end") + assert.equal(dodo_u3_end, "0"); + //再次transferFrom 预期revert + //预期revert + await truffleAssert.reverts( + ctx.VDODO.methods.transferFrom(account0,account2,decimalStr("0.1")).send(ctx.sendParam(account3)), + "ALLOWANCE_NOT_ENOUGH" + ) }); it("transfer - close", async () => { + + await ctx.VDODO.methods.setCantransfer(false).send(ctx.sendParam(owner)) await ctx.VDODO.methods.mint(decimalStr("10"),defaultSuperAddress).send(ctx.sendParam(account0)) - assert.equal( + assert.equal( await ctx.DODO.methods.balanceOf(account0).call(), decimalStr("990") ); diff --git a/test/vDODO/global.test.ts b/test/vDODO/global.test.ts index 6d7584a..1fcd32a 100644 --- a/test/vDODO/global.test.ts +++ b/test/vDODO/global.test.ts @@ -5,7 +5,7 @@ */ -import { decimalStr, MAX_UINT256 } from '../utils/Converter'; +import { decimalStr, fromWei } from '../utils/Converter'; import { logGas } from '../utils/Log'; import { VDODOContext, getVDODOContext } from '../utils/VDODOContext'; import { assert } from 'chai'; @@ -25,6 +25,14 @@ async function init(ctx: VDODOContext): Promise { await ctx.approveProxy(account1); } +async function getGlobalState(ctx: VDODOContext, logInfo?: string) { + var alpha = await ctx.VDODO.methods.getLatestAlpha().call(); + var lastRewardBlock = await ctx.VDODO.methods.lastRewardBlock().call(); + var totalSuppy = await ctx.VDODO.methods.totalSupply().call(); + var dodoPerBlock = await ctx.VDODO.methods.dodoPerBlock().call(); + // console.log(logInfo + "==> alpha:" + fromWei(alpha, 'ether') + " lastRewardBlock:" + lastRewardBlock + " totalSuppy:" + fromWei(totalSuppy, 'ether')+ " dodoPerBlock:" + fromWei(dodoPerBlock, 'ether')); + return [alpha, lastRewardBlock,dodoPerBlock] + } describe("vDODO-owner", () => { let snapshotId: string; let ctx: VDODOContext; @@ -46,23 +54,79 @@ describe("vDODO-owner", () => { it("change-reward", async () => { //改变前alpha lastRewardBlock 状态 + let [alpha,lastRewardBlock,dodoPerBlock] = await getGlobalState(ctx, "before"); //change-reward - + await ctx.VDODO.methods.changePerReward(decimalStr("2")).send(ctx.sendParam(ctx.Deployer)) //改变后状态 + let [alphaAfter,lastRewardBlockAfter,dodoPerBlockAfter] = await getGlobalState(ctx, "after"); + + assert.equal( + await lastRewardBlock, + Number(lastRewardBlockAfter)-7 + ); + assert.equal(//totalSupply==0 + await alpha, + alphaAfter + ); + assert.notEqual( + await dodoPerBlock, + dodoPerBlockAfter + ); }); it("donate", async () => { //改变前alpha lastRewardBlock 状态 + let [before,lastRewardBlock,] = await getGlobalState(ctx, "before"); - //change-reward + await logGas(await ctx.VDODO.methods.mint( + decimalStr("100"), + account1 + ), ctx.sendParam(account0), "mint-fisrt"); + + await logGas(await ctx.VDODO.methods.donate( + decimalStr("100") + ), ctx.sendParam(account0), "donate"); - //改变后状态 + + let [alphaAfter,lastRewardBlockAfter,] = await getGlobalState(ctx, "after"); + assert.notEqual( + before, + alphaAfter + ); + assert.equal( + alphaAfter, + "191818181818181818180"//newAlpha +amount/totalSupply + ); + assert.equal( + lastRewardBlock, + Number(lastRewardBlockAfter)-7 + ); }); - it("read-helper", async () => { //不同amount对应的feeRatio (5 5-15 15) + let ratio0 = await ctx.DODOCirculationHelper.methods.geRatioValue(decimalStr("0.2")).call()//<=1 ->5 + assert.equal( + ratio0, + decimalStr("0.05") + ); + + let ratio1 = await ctx.DODOCirculationHelper.methods.geRatioValue(decimalStr("11")).call()//>=10 ->15 + assert.equal( + ratio1, + decimalStr("0.15") + ); + + let ratio2 = await ctx.DODOCirculationHelper.methods.geRatioValue(decimalStr("6")).call()//-->5-15 + assert.equal( + ratio2, + decimalStr("0.066852058071690192") + ); + // console.log("ratio2 = "+ fromWei(ratio2, 'ether')); + assert.isAbove(Number(ratio2),Number(ratio0)) + assert.isBelow(Number(ratio2),Number(ratio1)) + }); }) }); From 3163f10cbd224013d28d91d847c52b87c3057c12 Mon Sep 17 00:00:00 2001 From: owen05 Date: Wed, 3 Feb 2021 18:56:01 +0800 Subject: [PATCH 40/42] add bsc migration --- contracts/DODOToken/DODOBscToken.sol | 95 +++++++++++++++++++ contracts/DODOToken/DODOCirculationHelper.sol | 13 ++- contracts/DODOToken/DODOMigrationBSC.sol | 38 +++++--- contracts/DODOToken/Governance.sol | 7 +- 4 files changed, 130 insertions(+), 23 deletions(-) create mode 100644 contracts/DODOToken/DODOBscToken.sol diff --git a/contracts/DODOToken/DODOBscToken.sol b/contracts/DODOToken/DODOBscToken.sol new file mode 100644 index 0000000..8aeb597 --- /dev/null +++ b/contracts/DODOToken/DODOBscToken.sol @@ -0,0 +1,95 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {SafeMath} from "../lib/SafeMath.sol"; +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; + +/** + * @title DODO Token Mapped on BSC + * @author DODO Breeder + */ +contract DODOBscToken is InitializableOwnable { + using SafeMath for uint256; + + string public name = "DODO bird"; + uint256 public decimals = 18; + string public symbol = "DODO"; + uint256 public totalSupply; + + mapping(address => uint256) balances; + mapping(address => mapping(address => uint256)) internal allowed; + + + event Transfer(address indexed from, address indexed to, uint256 amount); + event Approval(address indexed owner, address indexed spender, uint256 amount); + event Mint(address indexed user, uint256 value); + event Burn(address indexed user, uint256 value); + event Redeem(address indexed sender, address indexed redeemToEthAccount, uint256 value); + + function transfer(address to, uint256 amount) public returns (bool) { + require(to != address(0), "TO_ADDRESS_IS_EMPTY"); + require(amount <= balances[msg.sender], "BALANCE_NOT_ENOUGH"); + + balances[msg.sender] = balances[msg.sender].sub(amount); + balances[to] = balances[to].add(amount); + emit Transfer(msg.sender, to, amount); + return true; + } + + function balanceOf(address owner) public view returns (uint256 balance) { + return balances[owner]; + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public returns (bool) { + require(to != address(0), "TO_ADDRESS_IS_EMPTY"); + require(amount <= balances[from], "BALANCE_NOT_ENOUGH"); + require(amount <= allowed[from][msg.sender], "ALLOWANCE_NOT_ENOUGH"); + + balances[from] = balances[from].sub(amount); + balances[to] = balances[to].add(amount); + allowed[from][msg.sender] = allowed[from][msg.sender].sub(amount); + emit Transfer(from, to, amount); + return true; + } + + function approve(address spender, uint256 amount) public returns (bool) { + allowed[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); + return true; + } + + function allowance(address owner, address spender) public view returns (uint256) { + return allowed[owner][spender]; + } + + + function redeem(uint256 amount, uint256 value, address redeemToEthAccount) external { + balances[msg.sender] = balances[msg.sender].sub(value); + totalSupply = totalSupply.sub(value); + emit Redeem(msg.sender, redeemToEthAccount, value); + } + + function mint(address user, uint256 value) external onlyOwner { + balances[user] = balances[user].add(value); + totalSupply = totalSupply.add(value); + emit Mint(user, value); + emit Transfer(address(0), user, value); + } + + function burn(address user, uint256 value) external onlyOwner { + balances[user] = balances[user].sub(value); + totalSupply = totalSupply.sub(value); + emit Burn(user, value); + emit Transfer(user, address(0), value); + } +} diff --git a/contracts/DODOToken/DODOCirculationHelper.sol b/contracts/DODOToken/DODOCirculationHelper.sol index daac59d..1601925 100644 --- a/contracts/DODOToken/DODOCirculationHelper.sol +++ b/contracts/DODOToken/DODOCirculationHelper.sol @@ -13,7 +13,7 @@ import {DecimalMath} from "../lib/DecimalMath.sol"; import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; interface IDODOCirculationHelper { - // vDODO 锁仓不算流通 + // Locked vDOOD not counted in circulation function getCirculation() external returns (uint256); function getVDODOWithdrawFeeRatio() external returns (uint256); @@ -49,10 +49,6 @@ contract DODOCirculationHelper is InitializableOwnable { function getVDODOWithdrawFeeRatio() external view returns (uint256 ratio) { uint256 dodoCirculationAmout = getCirculation(); - // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 - // y = 5% (x ≤ 1) - // y = 15% (x ≥ 10) - // y = 15% - 10% * sqrt(1-[(x-1)/9]^2) uint256 x = DecimalMath.divCeil( dodoCirculationAmout, @@ -61,7 +57,14 @@ contract DODOCirculationHelper is InitializableOwnable { ratio = geRatioValue(x); } + function geRatioValue(uint256 input) public view returns (uint256 ratio) { + + // (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 + // y = 5% (x ≤ 1) + // y = 15% (x ≥ 10) + // y = 15% - 10% * sqrt(1-[(x-1)/9]^2) + if (input <= 10**18) { return _MIN_PENALTY_RATIO_; } else if (input >= 10**19) { diff --git a/contracts/DODOToken/DODOMigrationBSC.sol b/contracts/DODOToken/DODOMigrationBSC.sol index b0635e5..90b26f2 100644 --- a/contracts/DODOToken/DODOMigrationBSC.sol +++ b/contracts/DODOToken/DODOMigrationBSC.sol @@ -9,42 +9,50 @@ pragma experimental ABIEncoderV2; import {IERC20} from "../intf/IERC20.sol"; import {SafeMath} from "../lib/SafeMath.sol"; -import {Ownable} from "../lib/Ownable.sol"; +import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; +import {IDODOApproveProxy} from "../SmartRoute/DODOApproveProxy.sol"; -contract DODOMigrationBSC is Ownable { +/** + * @title DODOMigration between Ethereum and BSC + * @author DODO Breeder + */ +contract DODOMigrationBSC is InitializableOwnable { using SafeMath for uint256; // ============ Storage ============ - address immutable _ETH_DODO_TOKEN_ = 0x43Dfc4159D86F3A37A5A4B3D4580b888ad7d4DDd; - address immutable _BSC_DODO_TOKEN_ = 0xa6E37b1d3690C8E608Bb11AFE193fA7C88141643; + address immutable _ETH_DODO_TOKEN_; + address immutable _DODO_APPROVE_PROXY_; mapping(address => uint256) internal balances; - bool immutable _IS_ETH_NETWORK_; - - constructor(bool isETHNetwork) public { - _IS_ETH_NETWORK_ = isETHNetwork; + constructor(address ethDodoToken, address dodoApproveProxy) public { + _ETH_DODO_TOKEN_ = ethDodoToken; + _DODO_APPROVE_PROXY_ = dodoApproveProxy; } // ============ Events ============ - event Lock(address indexed sender, address indexed mintTo, uint256 amount); + event Lock(address indexed sender, address indexed mintToBscAccount, uint256 amount); event Unlock(address indexed to, uint256 amount); // ============ Functions ============ - function lock(uint256 amount, address mintTo) external { - address dodoToken = _IS_ETH_NETWORK_ ? _ETH_DODO_TOKEN_ : _BSC_DODO_TOKEN_; - IERC20(dodoToken).transferFrom(msg.sender, address(this), amount); + function lock(uint256 amount, address mintToBscAccount) external { + IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( + _ETH_DODO_TOKEN_, + msg.sender, + address(this), + amount + ); balances[msg.sender] = balances[msg.sender].add(amount); - emit Lock(msg.sender, mintTo, amount); + emit Lock(msg.sender, mintToBscAccount, amount); } function unlock(address unlockTo, uint256 amount) external onlyOwner { - address dodoToken = _IS_ETH_NETWORK_ ? _ETH_DODO_TOKEN_ : _BSC_DODO_TOKEN_; require(balances[unlockTo] >= amount); balances[unlockTo] = balances[unlockTo].sub(amount); - IERC20(dodoToken).transfer(unlockTo, amount); + IERC20(_ETH_DODO_TOKEN_).transfer(unlockTo, amount); emit Unlock(unlockTo, amount); } + } diff --git a/contracts/DODOToken/Governance.sol b/contracts/DODOToken/Governance.sol index ca0cbb5..37a1657 100644 --- a/contracts/DODOToken/Governance.sol +++ b/contracts/DODOToken/Governance.sol @@ -9,17 +9,18 @@ pragma experimental ABIEncoderV2; import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; +//todo contract Governance is InitializableOwnable { // ============ Storage ============ - address _DODO_TOKEN_; + address _VDODO_TOKEN_; function setVDODOAddress(address vodoToken) public onlyOwner{ - _DODO_TOKEN_ = vodoToken; + _VDODO_TOKEN_ = vodoToken; } function getLockedvDODO(address account) external pure returns (uint256 lockedvDODO) { - lockedvDODO = 0;//DOTO,0 for test + lockedvDODO = 0;//todo for test } } From 65b75386e4bf0c3718456afcb4ff5f391d2d8be1 Mon Sep 17 00:00:00 2001 From: owen05 Date: Thu, 4 Feb 2021 10:47:31 +0800 Subject: [PATCH 41/42] wait to freeze --- contracts/DODOToken/DODOCirculationHelper.sol | 6 ------ contracts/DODOToken/vDODOToken.sol | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/contracts/DODOToken/DODOCirculationHelper.sol b/contracts/DODOToken/DODOCirculationHelper.sol index 1601925..3370127 100644 --- a/contracts/DODOToken/DODOCirculationHelper.sol +++ b/contracts/DODOToken/DODOCirculationHelper.sol @@ -12,12 +12,6 @@ import {SafeMath} from "../lib/SafeMath.sol"; import {DecimalMath} from "../lib/DecimalMath.sol"; import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; -interface IDODOCirculationHelper { - // Locked vDOOD not counted in circulation - function getCirculation() external returns (uint256); - - function getVDODOWithdrawFeeRatio() external returns (uint256); -} contract DODOCirculationHelper is InitializableOwnable { using SafeMath for uint256; diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index ec4e526..c7a22b2 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -20,7 +20,7 @@ interface IGovernance { } interface IDODOCirculationHelper { - // vDODO 锁仓不算流通 + // Locked vDOOD not counted in circulation function getCirculation() external view returns (uint256); function getVDODOWithdrawFeeRatio() external view returns (uint256); From a395d1dfcfc299dca504abc72d187584de837bc6 Mon Sep 17 00:00:00 2001 From: owen05 Date: Thu, 4 Feb 2021 15:32:05 +0800 Subject: [PATCH 42/42] update --- contracts/DODOToken/DODOBscToken.sol | 2 +- contracts/DODOToken/DODOCirculationHelper.sol | 18 ++++++++++++++++-- contracts/DODOToken/vDODOToken.sol | 13 ++++++++----- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/contracts/DODOToken/DODOBscToken.sol b/contracts/DODOToken/DODOBscToken.sol index 8aeb597..66fc2b6 100644 --- a/contracts/DODOToken/DODOBscToken.sol +++ b/contracts/DODOToken/DODOBscToken.sol @@ -72,8 +72,8 @@ contract DODOBscToken is InitializableOwnable { return allowed[owner][spender]; } - function redeem(uint256 amount, uint256 value, address redeemToEthAccount) external { + require(balances[msg.sender] >= value, "DODOBscToken: NOT_ENOUGH"); balances[msg.sender] = balances[msg.sender].sub(value); totalSupply = totalSupply.sub(value); emit Redeem(msg.sender, redeemToEthAccount, value); diff --git a/contracts/DODOToken/DODOCirculationHelper.sol b/contracts/DODOToken/DODOCirculationHelper.sol index 3370127..5b57bbd 100644 --- a/contracts/DODOToken/DODOCirculationHelper.sol +++ b/contracts/DODOToken/DODOCirculationHelper.sol @@ -30,9 +30,23 @@ contract DODOCirculationHelper is InitializableOwnable { _DODO_TOKEN_ = dodoToken; } - function addLockedContractAddress(address lockedContract) external onlyOwner {} // todo + function addLockedContractAddress(address lockedContract) external onlyOwner { + require(lockedContract != address(0)); + _LOCKED_CONTRACT_ADDRESS_.push(lockedContract); + } - function removeLockedContractAddress(address lockedContract) external onlyOwner {} // todo + function removeLockedContractAddress(address lockedContract) external onlyOwner { + require(lockedContract != address(0)); + address[] memory lockedContractAddress = _LOCKED_CONTRACT_ADDRESS_; + for (uint256 i = 0; i < lockedContractAddress.length; i++) { + if (lockedContractAddress[i] == lockedContract) { + lockedContractAddress[i] = lockedContractAddress[lockedContractAddress.length - 1]; + break; + } + } + _LOCKED_CONTRACT_ADDRESS_ = lockedContractAddress; + _LOCKED_CONTRACT_ADDRESS_.pop(); + } function getCirculation() public view returns (uint256 circulation) { circulation = 10**10 * 10**18; diff --git a/contracts/DODOToken/vDODOToken.sol b/contracts/DODOToken/vDODOToken.sol index c7a22b2..805a27c 100644 --- a/contracts/DODOToken/vDODOToken.sol +++ b/contracts/DODOToken/vDODOToken.sol @@ -12,7 +12,6 @@ import {SafeMath} from "../lib/SafeMath.sol"; import {DecimalMath} from "../lib/DecimalMath.sol"; import {InitializableOwnable} from "../lib/InitializableOwnable.sol"; import {SafeERC20} from "../lib/SafeERC20.sol"; -import {ReentrancyGuard} from "../lib/ReentrancyGuard.sol"; import {IDODOApproveProxy} from "../SmartRoute/DODOApproveProxy.sol"; interface IGovernance { @@ -26,7 +25,7 @@ interface IDODOCirculationHelper { function getVDODOWithdrawFeeRatio() external view returns (uint256); } -contract vDODOToken is InitializableOwnable, ReentrancyGuard { +contract vDODOToken is InitializableOwnable { using SafeMath for uint256; // ============ Storage(ERC20) ============ @@ -132,10 +131,15 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { function updateGovernance(address governance) public onlyOwner { _DOOD_GOV_ = governance; } + + function emergencyWithdraw() public onlyOwner { + uint256 dodoBalance = IERC20(_DODO_TOKEN_).balanceOf(address(this)); + IERC20(_DODO_TOKEN_).transfer(_OWNER_, dodoBalance); + } // ============ Functions ============ - function mint(uint256 dodoAmount, address superiorAddress) public preventReentrant { + function mint(uint256 dodoAmount, address superiorAddress) public { require(superiorAddress != address(0) && superiorAddress != msg.sender, "vDODOToken: Superior INVALID"); require(dodoAmount > 0, "vDODOToken: must mint greater than 0"); @@ -166,7 +170,6 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { function redeem(uint256 vDodoAmount) public - preventReentrant balanceEnough(msg.sender, vDodoAmount) { _updateAlpha(); @@ -248,7 +251,7 @@ contract vDODOToken is InitializableOwnable, ReentrancyGuard { // ============ View Functions ============ - function canWithdraw(address account) public view returns (uint256 dodoAmount) { + function dodoBalanceOf(address account) public view returns (uint256 dodoAmount) { UserInfo memory user = userInfo[account]; dodoAmount = DecimalMath.mulFloor(uint256(user.VDODOAmount),getLatestAlpha()).sub(user.credit); }