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