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 1/9] 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 2/9] 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 3/9] 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 4/9] 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 5/9] 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 6/9] 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 7/9] 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 8/9] 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 9/9] 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