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] 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