1058 lines
36 KiB
Solidity
1058 lines
36 KiB
Solidity
/*
|
||
|
||
Copyright 2020 DODO ZOO.
|
||
SPDX-License-Identifier: Apache-2.0
|
||
|
||
*/
|
||
|
||
pragma solidity 0.6.9;
|
||
pragma experimental ABIEncoderV2;
|
||
|
||
/**
|
||
* @title SafeMath
|
||
* @author DODO Breeder
|
||
*
|
||
* @notice Math operations with safety checks that revert on error
|
||
*/
|
||
library SafeMath {
|
||
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
|
||
if (a == 0) {
|
||
return 0;
|
||
}
|
||
|
||
uint256 c = a * b;
|
||
require(c / a == b, "MUL_ERROR");
|
||
|
||
return c;
|
||
}
|
||
|
||
function div(uint256 a, uint256 b) internal pure returns (uint256) {
|
||
require(b > 0, "DIVIDING_ERROR");
|
||
return a / b;
|
||
}
|
||
|
||
function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
|
||
uint256 quotient = div(a, b);
|
||
uint256 remainder = a - quotient * b;
|
||
if (remainder > 0) {
|
||
return quotient + 1;
|
||
} else {
|
||
return quotient;
|
||
}
|
||
}
|
||
|
||
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
|
||
require(b <= a, "SUB_ERROR");
|
||
return a - b;
|
||
}
|
||
|
||
function add(uint256 a, uint256 b) internal pure returns (uint256) {
|
||
uint256 c = a + b;
|
||
require(c >= a, "ADD_ERROR");
|
||
return c;
|
||
}
|
||
|
||
function sqrt(uint256 x) internal pure returns (uint256 y) {
|
||
uint256 z = x / 2 + 1;
|
||
y = x;
|
||
while (z < y) {
|
||
y = z;
|
||
z = (x / z + z) / 2;
|
||
}
|
||
}
|
||
}
|
||
|
||
pragma solidity 0.6.9;
|
||
/**
|
||
* @title DecimalMath
|
||
* @author DODO Breeder
|
||
*
|
||
* @notice Functions for fixed point number with 18 decimals
|
||
*/
|
||
library DecimalMath {
|
||
using SafeMath for uint256;
|
||
|
||
uint256 internal constant ONE = 10**18;
|
||
uint256 internal constant ONE2 = 10**36;
|
||
|
||
function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) {
|
||
return target.mul(d) / (10**18);
|
||
}
|
||
|
||
function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) {
|
||
return target.mul(d).divCeil(10**18);
|
||
}
|
||
|
||
function divFloor(uint256 target, uint256 d) internal pure returns (uint256) {
|
||
return target.mul(10**18).div(d);
|
||
}
|
||
|
||
function divCeil(uint256 target, uint256 d) internal pure returns (uint256) {
|
||
return target.mul(10**18).divCeil(d);
|
||
}
|
||
|
||
function reciprocalFloor(uint256 target) internal pure returns (uint256) {
|
||
return uint256(10**36).div(target);
|
||
}
|
||
|
||
function reciprocalCeil(uint256 target) internal pure returns (uint256) {
|
||
return uint256(10**36).divCeil(target);
|
||
}
|
||
}
|
||
// File: @openzeppelin/contracts/access/Ownable.sol
|
||
|
||
|
||
|
||
pragma solidity 0.6.9;
|
||
/**
|
||
* @title Ownable
|
||
* @author DODO Breeder
|
||
*
|
||
* @notice Ownership related functions
|
||
*/
|
||
contract Ownable {
|
||
address public _OWNER_;
|
||
address public _NEW_OWNER_;
|
||
|
||
// ============ Events ============
|
||
|
||
event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
|
||
|
||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||
|
||
// ============ Modifiers ============
|
||
|
||
modifier onlyOwner() {
|
||
require(msg.sender == _OWNER_, "NOT_OWNER");
|
||
_;
|
||
}
|
||
|
||
// ============ Functions ============
|
||
|
||
constructor() internal {
|
||
_OWNER_ = msg.sender;
|
||
emit OwnershipTransferred(address(0), _OWNER_);
|
||
}
|
||
|
||
function transferOwnership(address newOwner) external onlyOwner {
|
||
emit OwnershipTransferPrepared(_OWNER_, newOwner);
|
||
_NEW_OWNER_ = newOwner;
|
||
}
|
||
|
||
function claimOwnership() external {
|
||
require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
|
||
emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
|
||
_OWNER_ = _NEW_OWNER_;
|
||
_NEW_OWNER_ = address(0);
|
||
}
|
||
}
|
||
|
||
|
||
pragma solidity 0.6.9;
|
||
|
||
/**
|
||
* @title ReentrancyGuard
|
||
* @author DODO Breeder
|
||
*
|
||
* @notice Protect functions from Reentrancy Attack
|
||
*/
|
||
contract ReentrancyGuard {
|
||
// https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=zero-state#scoping-and-declarations
|
||
// zero-state of _ENTERED_ is false
|
||
bool private _ENTERED_;
|
||
|
||
modifier preventReentrant() {
|
||
require(!_ENTERED_, "REENTRANT");
|
||
_ENTERED_ = true;
|
||
_;
|
||
_ENTERED_ = false;
|
||
}
|
||
}
|
||
|
||
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol
|
||
|
||
|
||
|
||
pragma solidity 0.6.9;
|
||
|
||
/**
|
||
* @dev Interface of the ERC20 standard as defined in the EIP.
|
||
*/
|
||
interface IERC20 {
|
||
/**
|
||
* @dev Returns the amount of tokens in existence.
|
||
*/
|
||
function totalSupply() external view returns (uint256);
|
||
|
||
/**
|
||
* @dev Returns the amount of tokens owned by `account`.
|
||
*/
|
||
function balanceOf(address account) external view returns (uint256);
|
||
|
||
/**
|
||
* @dev Moves `amount` tokens from the caller's account to `recipient`.
|
||
*
|
||
* Returns a boolean value indicating whether the operation succeeded.
|
||
*
|
||
* Emits a {Transfer} event.
|
||
*/
|
||
function transfer(address recipient, uint256 amount) external returns (bool);
|
||
|
||
/**
|
||
* @dev Returns the remaining number of tokens that `spender` will be
|
||
* allowed to spend on behalf of `owner` through {transferFrom}. This is
|
||
* zero by default.
|
||
*
|
||
* This value changes when {approve} or {transferFrom} are called.
|
||
*/
|
||
function allowance(address owner, address spender) external view returns (uint256);
|
||
|
||
/**
|
||
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
|
||
*
|
||
* Returns a boolean value indicating whether the operation succeeded.
|
||
*
|
||
* IMPORTANT: Beware that changing an allowance with this method brings the risk
|
||
* that someone may use both the old and the new allowance by unfortunate
|
||
* transaction ordering. One possible solution to mitigate this race
|
||
* condition is to first reduce the spender's allowance to 0 and set the
|
||
* desired value afterwards:
|
||
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||
*
|
||
* Emits an {Approval} event.
|
||
*/
|
||
function approve(address spender, uint256 amount) external returns (bool);
|
||
|
||
/**
|
||
* @dev Moves `amount` tokens from `sender` to `recipient` using the
|
||
* allowance mechanism. `amount` is then deducted from the caller's
|
||
* allowance.
|
||
*
|
||
* Returns a boolean value indicating whether the operation succeeded.
|
||
*
|
||
* Emits a {Transfer} event.
|
||
*/
|
||
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
|
||
|
||
/**
|
||
* @dev Emitted when `value` tokens are moved from one account (`from`) to
|
||
* another (`to`).
|
||
*
|
||
* Note that `value` may be zero.
|
||
*/
|
||
event Transfer(address indexed from, address indexed to, uint256 value);
|
||
|
||
/**
|
||
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
|
||
* a call to {approve}. `value` is the new allowance.
|
||
*/
|
||
event Approval(address indexed owner, address indexed spender, uint256 value);
|
||
}
|
||
|
||
pragma solidity 0.6.9;
|
||
|
||
/*
|
||
* @dev Provides information about the current execution context, including the
|
||
* sender of the transaction and its data. While these are generally available
|
||
* via msg.sender and msg.data, they should not be accessed in such a direct
|
||
* manner, since when dealing with GSN meta-transactions the account sending and
|
||
* paying for execution may not be the actual sender (as far as an application
|
||
* is concerned).
|
||
*
|
||
* This contract is only required for intermediate, library-like contracts.
|
||
*/
|
||
abstract contract Context {
|
||
function _msgSender() internal view virtual returns (address payable) {
|
||
return msg.sender;
|
||
}
|
||
|
||
function _msgData() internal view virtual returns (bytes memory) {
|
||
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
|
||
return msg.data;
|
||
}
|
||
}
|
||
|
||
pragma solidity 0.6.9;
|
||
/**
|
||
* @title SafeERC20
|
||
* @dev Wrappers around ERC20 operations that throw on failure (when the token
|
||
* contract returns false). Tokens that return no value (and instead revert or
|
||
* throw on failure) are also supported, non-reverting calls are assumed to be
|
||
* successful.
|
||
* To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
|
||
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
|
||
*/
|
||
library SafeERC20 {
|
||
using SafeMath for uint256;
|
||
|
||
function safeTransfer(
|
||
IERC20 token,
|
||
address to,
|
||
uint256 value
|
||
) internal {
|
||
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
|
||
}
|
||
|
||
function safeTransferFrom(
|
||
IERC20 token,
|
||
address from,
|
||
address to,
|
||
uint256 value
|
||
) internal {
|
||
_callOptionalReturn(
|
||
token,
|
||
abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
|
||
);
|
||
}
|
||
|
||
function safeApprove(
|
||
IERC20 token,
|
||
address spender,
|
||
uint256 value
|
||
) internal {
|
||
// safeApprove should only be called when setting an initial allowance,
|
||
// or when resetting it to zero. To increase and decrease it, use
|
||
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
|
||
// solhint-disable-next-line max-line-length
|
||
require(
|
||
(value == 0) || (token.allowance(address(this), spender) == 0),
|
||
"SafeERC20: approve from non-zero to non-zero allowance"
|
||
);
|
||
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
|
||
}
|
||
|
||
/**
|
||
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
|
||
* on the return value: the return value is optional (but if data is returned, it must not be false).
|
||
* @param token The token targeted by the call.
|
||
* @param data The call data (encoded using abi.encode or one of its variants).
|
||
*/
|
||
function _callOptionalReturn(IERC20 token, bytes memory data) private {
|
||
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
|
||
// we're implementing it ourselves.
|
||
|
||
// A Solidity high level call has three parts:
|
||
// 1. The target address is checked to verify it contains contract code
|
||
// 2. The call itself is made, and success asserted
|
||
// 3. The return value is decoded, which in turn checks the size of the returned data.
|
||
// solhint-disable-next-line max-line-length
|
||
|
||
// solhint-disable-next-line avoid-low-level-calls
|
||
(bool success, bytes memory returndata) = address(token).call(data);
|
||
require(success, "SafeERC20: low-level call failed");
|
||
|
||
if (returndata.length > 0) {
|
||
// Return data is optional
|
||
// solhint-disable-next-line max-line-length
|
||
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
|
||
}
|
||
}
|
||
}
|
||
|
||
// File: @openzeppelin/contracts/utils/Address.sol
|
||
pragma solidity 0.6.9;
|
||
|
||
/**
|
||
* @dev Collection of functions related to the address type
|
||
*/
|
||
library Address {
|
||
/**
|
||
* @dev Returns true if `account` is a contract.
|
||
*
|
||
* [IMPORTANT]
|
||
* ====
|
||
* It is unsafe to assume that an address for which this function returns
|
||
* false is an externally-owned account (EOA) and not a contract.
|
||
*
|
||
* Among others, `isContract` will return false for the following
|
||
* types of addresses:
|
||
*
|
||
* - an externally-owned account
|
||
* - a contract in construction
|
||
* - an address where a contract will be created
|
||
* - an address where a contract lived, but was destroyed
|
||
* ====
|
||
*/
|
||
function isContract(address account) internal view returns (bool) {
|
||
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
|
||
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
|
||
// for accounts without code, i.e. `keccak256('')`
|
||
bytes32 codehash;
|
||
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
|
||
// solhint-disable-next-line no-inline-assembly
|
||
assembly { codehash := extcodehash(account) }
|
||
return (codehash != accountHash && codehash != 0x0);
|
||
}
|
||
|
||
/**
|
||
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
|
||
* `recipient`, forwarding all available gas and reverting on errors.
|
||
*
|
||
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
|
||
* of certain opcodes, possibly making contracts go over the 2300 gas limit
|
||
* imposed by `transfer`, making them unable to receive funds via
|
||
* `transfer`. {sendValue} removes this limitation.
|
||
*
|
||
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
|
||
*
|
||
* IMPORTANT: because control is transferred to `recipient`, care must be
|
||
* taken to not create reentrancy vulnerabilities. Consider using
|
||
* {ReentrancyGuard} or the
|
||
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
|
||
*/
|
||
function sendValue(address payable recipient, uint256 amount) internal {
|
||
require(address(this).balance >= amount, "Address: insufficient balance");
|
||
|
||
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
|
||
(bool success, ) = recipient.call{ value: amount }("");
|
||
require(success, "Address: unable to send value, recipient may have reverted");
|
||
}
|
||
|
||
/**
|
||
* @dev Performs a Solidity function call using a low level `call`. A
|
||
* plain`call` is an unsafe replacement for a function call: use this
|
||
* function instead.
|
||
*
|
||
* If `target` reverts with a revert reason, it is bubbled up by this
|
||
* function (like regular Solidity function calls).
|
||
*
|
||
* Returns the raw returned data. To convert to the expected return value,
|
||
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
|
||
*
|
||
* Requirements:
|
||
*
|
||
* - `target` must be a contract.
|
||
* - calling `target` with `data` must not revert.
|
||
*
|
||
* _Available since v3.1._
|
||
*/
|
||
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
|
||
return functionCall(target, data, "Address: low-level call failed");
|
||
}
|
||
|
||
/**
|
||
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
|
||
* `errorMessage` as a fallback revert reason when `target` reverts.
|
||
*
|
||
* _Available since v3.1._
|
||
*/
|
||
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
|
||
return _functionCallWithValue(target, data, 0, errorMessage);
|
||
}
|
||
|
||
/**
|
||
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
|
||
* but also transferring `value` wei to `target`.
|
||
*
|
||
* Requirements:
|
||
*
|
||
* - the calling contract must have an ETH balance of at least `value`.
|
||
* - the called Solidity function must be `payable`.
|
||
*
|
||
* _Available since v3.1._
|
||
*/
|
||
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
|
||
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
|
||
}
|
||
|
||
/**
|
||
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
|
||
* with `errorMessage` as a fallback revert reason when `target` reverts.
|
||
*
|
||
* _Available since v3.1._
|
||
*/
|
||
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
|
||
require(address(this).balance >= value, "Address: insufficient balance for call");
|
||
return _functionCallWithValue(target, data, value, errorMessage);
|
||
}
|
||
|
||
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
|
||
require(isContract(target), "Address: call to non-contract");
|
||
|
||
// solhint-disable-next-line avoid-low-level-calls
|
||
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
|
||
if (success) {
|
||
return returndata;
|
||
} else {
|
||
// Look for revert reason and bubble it up if present
|
||
if (returndata.length > 0) {
|
||
// The easiest way to bubble the revert reason is using memory via assembly
|
||
|
||
// solhint-disable-next-line no-inline-assembly
|
||
assembly {
|
||
let returndata_size := mload(returndata)
|
||
revert(add(32, returndata), returndata_size)
|
||
}
|
||
} else {
|
||
revert(errorMessage);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
pragma solidity 0.6.9;
|
||
|
||
/**
|
||
* @dev Implementation of the {IERC20} interface.
|
||
*
|
||
* This implementation is agnostic to the way tokens are created. This means
|
||
* that a supply mechanism has to be added in a derived contract using {_mint}.
|
||
* For a generic mechanism see {ERC20PresetMinterPauser}.
|
||
*
|
||
* TIP: For a detailed writeup see our guide
|
||
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
|
||
* to implement supply mechanisms].
|
||
*
|
||
* We have followed general OpenZeppelin guidelines: functions revert instead
|
||
* of returning `false` on failure. This behavior is nonetheless conventional
|
||
* and does not conflict with the expectations of ERC20 applications.
|
||
*
|
||
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
|
||
* This allows applications to reconstruct the allowance for all accounts just
|
||
* by listening to said events. Other implementations of the EIP may not emit
|
||
* these events, as it isn't required by the specification.
|
||
*
|
||
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
|
||
* functions have been added to mitigate the well-known issues around setting
|
||
* allowances. See {IERC20-approve}.
|
||
*/
|
||
contract ERC20 is Context, IERC20 {
|
||
using SafeMath for uint256;
|
||
using Address for address;
|
||
|
||
bool cantransfer;
|
||
uint256 private _totalSupply;
|
||
string private _name;
|
||
string private _symbol;
|
||
uint8 private _decimals;
|
||
|
||
mapping (address => uint256) private _balances;
|
||
mapping (address => mapping (address => uint256)) private _allowances;
|
||
mapping(address => bool) public operater;
|
||
// ============ Modifiers ============
|
||
modifier onlyOperater() {
|
||
require(cantransfer || operater[msg.sender] , "not allowed transfer");
|
||
_;
|
||
}
|
||
|
||
/**
|
||
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
|
||
* a default value of 18.
|
||
*
|
||
* To select a different value for {decimals}, use {_setupDecimals}.
|
||
*
|
||
* All three of these values are immutable: they can only be set once during
|
||
* construction.
|
||
*/
|
||
constructor (string memory name, string memory symbol) public {
|
||
_name = name;
|
||
_symbol = symbol;
|
||
_decimals = 18;
|
||
}
|
||
|
||
/**
|
||
* @dev Returns the name of the token.
|
||
*/
|
||
function name() public view returns (string memory) {
|
||
return _name;
|
||
}
|
||
|
||
/**
|
||
* @dev Returns the symbol of the token, usually a shorter version of the
|
||
* name.
|
||
*/
|
||
function symbol() public view returns (string memory) {
|
||
return _symbol;
|
||
}
|
||
|
||
/**
|
||
* @dev Returns the number of decimals used to get its user representation.
|
||
* For example, if `decimals` equals `2`, a balance of `505` tokens should
|
||
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
|
||
*
|
||
* Tokens usually opt for a value of 18, imitating the relationship between
|
||
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
|
||
* called.
|
||
*
|
||
* NOTE: This information is only used for _display_ purposes: it in
|
||
* no way affects any of the arithmetic of the contract, including
|
||
* {IERC20-balanceOf} and {IERC20-transfer}.
|
||
*/
|
||
function decimals() public view returns (uint8) {
|
||
return _decimals;
|
||
}
|
||
|
||
/**
|
||
* @dev See {IERC20-totalSupply}.
|
||
*/
|
||
function totalSupply() public view override returns (uint256) {
|
||
return _totalSupply;
|
||
}
|
||
|
||
/**
|
||
* @dev See {IERC20-balanceOf}.
|
||
*/
|
||
function balanceOf(address account) public view virtual override returns (uint256) {
|
||
return _balances[account];
|
||
}
|
||
|
||
/**
|
||
* @dev See {IERC20-transfer}.
|
||
*
|
||
* Requirements:
|
||
*
|
||
* - `recipient` cannot be the zero address.
|
||
* - the caller must have a balance of at least `amount`.
|
||
*/
|
||
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
|
||
_transfer(_msgSender(), recipient, amount);
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @dev See {IERC20-allowance}.
|
||
*/
|
||
function allowance(address owner, address spender) public view virtual override returns (uint256) {
|
||
return _allowances[owner][spender];
|
||
}
|
||
|
||
/**
|
||
* @dev See {IERC20-approve}.
|
||
*
|
||
* Requirements:
|
||
*
|
||
* - `spender` cannot be the zero address.
|
||
*/
|
||
function approve(address spender, uint256 amount) public virtual override returns (bool) {
|
||
_approve(_msgSender(), spender, amount);
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @dev See {IERC20-transferFrom}.
|
||
*
|
||
* Emits an {Approval} event indicating the updated allowance. This is not
|
||
* required by the EIP. See the note at the beginning of {ERC20};
|
||
*
|
||
* Requirements:
|
||
* - `sender` and `recipient` cannot be the zero address.
|
||
* - `sender` must have a balance of at least `amount`.
|
||
* - the caller must have allowance for ``sender``'s tokens of at least
|
||
* `amount`.
|
||
*/
|
||
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
|
||
_transfer(sender, recipient, amount);
|
||
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount));
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @dev Atomically increases the allowance granted to `spender` by the caller.
|
||
*
|
||
* This is an alternative to {approve} that can be used as a mitigation for
|
||
* problems described in {IERC20-approve}.
|
||
*
|
||
* Emits an {Approval} event indicating the updated allowance.
|
||
*
|
||
* Requirements:
|
||
*
|
||
* - `spender` cannot be the zero address.
|
||
*/
|
||
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
|
||
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @dev Atomically decreases the allowance granted to `spender` by the caller.
|
||
*
|
||
* This is an alternative to {approve} that can be used as a mitigation for
|
||
* problems described in {IERC20-approve}.
|
||
*
|
||
* Emits an {Approval} event indicating the updated allowance.
|
||
*
|
||
* Requirements:
|
||
*
|
||
* - `spender` cannot be the zero address.
|
||
* - `spender` must have allowance for the caller of at least
|
||
* `subtractedValue`.
|
||
*/
|
||
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
|
||
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue));
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @dev Moves tokens `amount` from `sender` to `recipient`.
|
||
*
|
||
* This is internal function is equivalent to {transfer}, and can be used to
|
||
* e.g. implement automatic token fees, slashing mechanisms, etc.
|
||
*
|
||
* Emits a {Transfer} event.
|
||
*
|
||
* Requirements:
|
||
*
|
||
* - `sender` cannot be the zero address.
|
||
* - `recipient` cannot be the zero address.
|
||
* - `sender` must have a balance of at least `amount`.
|
||
*/
|
||
function _transfer(address sender, address recipient, uint256 amount) internal onlyOperater virtual {
|
||
require(sender != address(0), "ERC20: transfer from the zero address");
|
||
require(recipient != address(0), "ERC20: transfer to the zero address");
|
||
|
||
_beforeTokenTransfer(sender, recipient, amount);
|
||
|
||
_balances[sender] = _balances[sender].sub(amount);
|
||
_balances[recipient] = _balances[recipient].add(amount);
|
||
emit Transfer(sender, recipient, amount);
|
||
}
|
||
|
||
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
|
||
* the total supply.
|
||
*
|
||
* Emits a {Transfer} event with `from` set to the zero address.
|
||
*
|
||
* Requirements
|
||
*
|
||
* - `to` cannot be the zero address.
|
||
*/
|
||
function _mint(address account, uint256 amount) internal virtual {
|
||
require(account != address(0), "ERC20: mint to the zero address");
|
||
|
||
_beforeTokenTransfer(address(0), account, amount);
|
||
|
||
_totalSupply = _totalSupply.add(amount);
|
||
_balances[account] = _balances[account].add(amount);
|
||
emit Transfer(address(0), account, amount);
|
||
}
|
||
|
||
/**
|
||
* @dev Destroys `amount` tokens from `account`, reducing the
|
||
* total supply.
|
||
*
|
||
* Emits a {Transfer} event with `to` set to the zero address.
|
||
*
|
||
* Requirements
|
||
*
|
||
* - `account` cannot be the zero address.
|
||
* - `account` must have at least `amount` tokens.
|
||
*/
|
||
function _burn(address account, uint256 amount) internal virtual {
|
||
require(account != address(0), "ERC20: burn from the zero address");
|
||
|
||
_beforeTokenTransfer(account, address(0), amount);
|
||
|
||
_balances[account] = _balances[account].sub(amount);
|
||
_totalSupply = _totalSupply.sub(amount);
|
||
emit Transfer(account, address(0), amount);
|
||
}
|
||
|
||
/**
|
||
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
|
||
*
|
||
* This is internal function is equivalent to `approve`, and can be used to
|
||
* e.g. set automatic allowances for certain subsystems, etc.
|
||
*
|
||
* Emits an {Approval} event.
|
||
*
|
||
* Requirements:
|
||
*
|
||
* - `owner` cannot be the zero address.
|
||
* - `spender` cannot be the zero address.
|
||
*/
|
||
function _approve(address owner, address spender, uint256 amount) internal onlyOperater virtual {
|
||
require(owner != address(0), "ERC20: approve from the zero address");
|
||
require(spender != address(0), "ERC20: approve to the zero address");
|
||
|
||
_allowances[owner][spender] = amount;
|
||
emit Approval(owner, spender, amount);
|
||
}
|
||
|
||
/**
|
||
* @dev Sets {decimals} to a value other than the default one of 18.
|
||
*
|
||
* WARNING: This function should only be called from the constructor. Most
|
||
* applications that interact with token contracts will not expect
|
||
* {decimals} to ever change, and may work incorrectly if it does.
|
||
*/
|
||
function _setupDecimals(uint8 decimals_) internal {
|
||
_decimals = decimals_;
|
||
}
|
||
|
||
function _setCantransfer(bool _cantransfer) internal {
|
||
cantransfer = _cantransfer;
|
||
}
|
||
|
||
function _addOperation(address _operater) internal {
|
||
operater[_operater] = true;
|
||
}
|
||
function _removeOperation(address _operater) internal {
|
||
operater[_operater] = false;
|
||
}
|
||
|
||
/**
|
||
* @dev Hook that is called before any transfer of tokens. This includes
|
||
* minting and burning.
|
||
*
|
||
* Calling conditions:
|
||
*
|
||
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
|
||
* will be to transferred to `to`.
|
||
* - when `from` is zero, `amount` tokens will be minted for `to`.
|
||
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
|
||
* - `from` and `to` are never both zero.
|
||
*
|
||
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
|
||
*/
|
||
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
|
||
}
|
||
pragma solidity 0.6.9;
|
||
interface IGovernance {
|
||
function governanceCall(address account, uint256 amount,bytes calldata data) external returns (bool);
|
||
|
||
}
|
||
pragma solidity 0.6.9;
|
||
contract SpToken is ERC20("StakingPowerToken", "SPT"), Ownable ,ReentrancyGuard{
|
||
using SafeMath for uint256;
|
||
using SafeERC20 for IERC20;
|
||
|
||
address govAddr;
|
||
IERC20 dodo;
|
||
|
||
uint256 public alpha = 1;
|
||
uint256 public totalSp;
|
||
uint256 public totalOverdraft;
|
||
|
||
uint256 public _DODOPERBLOCK_ = 1e18;//TODO
|
||
uint256 constant public _MAG_SP_AMOUNT_ = 10;
|
||
uint256 constant public _MAG_TOTALSP_AMOUNT_ = 110;
|
||
uint256 constant public _BASE_AMOUNT_ = 100;
|
||
|
||
struct UserInfo {
|
||
uint256 dodoAmount;
|
||
address upline;
|
||
uint256 spAmount;
|
||
uint256 overdraft;
|
||
uint256 lastRewardBlock;
|
||
uint256 totalRedeem;
|
||
bool hasParticipateGov; //是否正在参与治理,是的话就不可以提币
|
||
}
|
||
|
||
mapping (address => UserInfo) public userInfo;
|
||
|
||
// ============ Events ============
|
||
event ParticipatoryGov(address indexed user, uint256 amount);
|
||
event Deposit(address indexed user,address indexed upline, uint256 amount);
|
||
event Redeem(address indexed user, uint256 amount);
|
||
event SetCantransfer(bool allowed);
|
||
event RemoveOperation(address indexed _operater);
|
||
event AddOperation(address indexed _operater);
|
||
|
||
receive() external payable {
|
||
revert();
|
||
}
|
||
// ============ Functions ============
|
||
constructor(
|
||
address _govAddr,
|
||
address _dodo
|
||
|
||
) public {
|
||
govAddr = _govAddr;
|
||
dodo = IERC20(_dodo);
|
||
}
|
||
|
||
|
||
function mint(address _to, uint256 _amount) public onlyOwner {
|
||
_mint(_to, _amount);
|
||
}
|
||
function burn(address _to, uint256 amount) public onlyOwner{
|
||
_burn(_to,amount);
|
||
}
|
||
function setCantransfer(bool _allowed) public onlyOwner {
|
||
_setCantransfer(_allowed);
|
||
emit SetCantransfer(_allowed);
|
||
}
|
||
function addOperationAddress(address _operationAddress) public onlyOwner {
|
||
_addOperation(_operationAddress);
|
||
emit AddOperation(_operationAddress);
|
||
}
|
||
function removeOperation(address _operationAddress) public onlyOwner {
|
||
_removeOperation(_operationAddress);
|
||
emit RemoveOperation(_operationAddress);
|
||
}
|
||
|
||
function participatoryGov(
|
||
uint256 _amount,
|
||
bytes calldata _data
|
||
) external preventReentrant {
|
||
UserInfo storage user = userInfo[msg.sender];
|
||
require(user.spAmount>_amount,"no enough sp");
|
||
if (_data.length > 0)
|
||
IGovernance(govAddr).governanceCall(msg.sender, _amount, _data);
|
||
user.spAmount = user.spAmount.sub(_amount);
|
||
user.hasParticipateGov = true;
|
||
emit ParticipatoryGov(msg.sender, _amount);
|
||
}
|
||
|
||
//TODO _uplineAddress??? deposit again????
|
||
//TODO round up /down
|
||
function deposit(uint256 _amount,address _uplineAddress) public preventReentrant{
|
||
require(_amount>0,"must deposit greater than 0");
|
||
|
||
dodo.transferFrom(msg.sender, address(this), _amount);
|
||
|
||
UserInfo storage user = userInfo[msg.sender];
|
||
if(user.dodoAmount==0){
|
||
user.lastRewardBlock = block.number;
|
||
}
|
||
user.dodoAmount = user.dodoAmount.add(_amount);
|
||
// accuDODO = _DODOPERBLOCK_*(block-lastRewardBlock)
|
||
uint256 accuDODO = _DODOPERBLOCK_ * (block.number.sub(user.lastRewardBlock));
|
||
//TODO FIRST DEPOSIT???
|
||
if(totalSp > 0){
|
||
// alpha = alpha + accuDODO/totalSp (round down)
|
||
alpha = alpha.add(accuDODO.div(totalSp));
|
||
}
|
||
|
||
// 自己的sp + x/alpha
|
||
uint256 newSpAmount = _amount.div(alpha);
|
||
|
||
_mint(msg.sender,newSpAmount);
|
||
// spToken.mint(msg.sender,newSpAmount);
|
||
|
||
user.spAmount = user.spAmount.add(newSpAmount);
|
||
if(user.upline==address(0x0)){
|
||
user.upline = _uplineAddress;
|
||
}
|
||
UserInfo storage uplineUser = userInfo[user.upline];
|
||
// 上级sp +( x/alpha)* 0.1 (round up)
|
||
uplineUser.spAmount = uplineUser.spAmount.add(
|
||
_amount.mul(alpha)
|
||
.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_));
|
||
|
||
// 上级DODO欠款 + x*0.1 (round up)
|
||
uint256 overdraft = _amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_);
|
||
uplineUser.overdraft = uplineUser.overdraft.add(overdraft);
|
||
|
||
totalOverdraft = totalOverdraft.add(overdraft);
|
||
// total sp + x/alpha*1.1
|
||
totalSp = totalSp.add(_amount.div(alpha).mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_));
|
||
emit Deposit(msg.sender,_uplineAddress, _amount);
|
||
}
|
||
//TODO round up /down
|
||
|
||
function redeem(uint256 _amount) public preventReentrant{
|
||
UserInfo storage user = userInfo[msg.sender];
|
||
require(user.spAmount>_amount,"no enough sp token");
|
||
require(!user.hasParticipateGov,"hasParticipateGov");
|
||
|
||
|
||
// accuDODO = _DODOPERBLOCK_*(block-lastRewardBlock)
|
||
uint256 accuDODO = _DODOPERBLOCK_ * (block.number.sub(user.lastRewardBlock));
|
||
// alpha = alpha + accuDODO/totalSp (round down)
|
||
alpha = alpha.add(accuDODO.div(totalSp));
|
||
// 自己的sp - x
|
||
_burn(msg.sender,_amount);
|
||
// spToken.burn(msg.sender,_amount);
|
||
|
||
user.spAmount = user.spAmount.sub(_amount);
|
||
|
||
UserInfo storage uplineUser = userInfo[user.upline];
|
||
// 上级sp - (x)*0.1(round down)
|
||
uplineUser.spAmount = uplineUser.spAmount.sub(
|
||
_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_));
|
||
|
||
// 上级DODO欠款 - x*alpha*0.1 (round down)
|
||
uint256 overdraft = _amount.mul(alpha).mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_);
|
||
uplineUser.overdraft = uplineUser.overdraft.sub(overdraft);
|
||
|
||
totalOverdraft = totalOverdraft.sub(overdraft);
|
||
|
||
// total sp - x*1.1
|
||
totalSp = totalSp.sub(_amount.mul(_MAG_TOTALSP_AMOUNT_).div(_BASE_AMOUNT_));
|
||
|
||
user.lastRewardBlock = block.number;
|
||
|
||
uint256 feeRatio = checkReward(_amount);
|
||
|
||
// x * 80% transfer to user
|
||
uint256 receiveAmount = _amount.mul(_BASE_AMOUNT_.sub(feeRatio).div(_BASE_AMOUNT_));
|
||
|
||
dodo.safeTransferFrom(address(this), msg.sender, receiveAmount);
|
||
user.dodoAmount = user.dodoAmount.sub(receiveAmount);
|
||
user.totalRedeem = user.totalRedeem.add(receiveAmount);
|
||
|
||
// alpha = alpha + x * 20% /totalSp
|
||
uint256 feeAmount = _amount.mul(feeRatio.div(_BASE_AMOUNT_));
|
||
alpha = alpha.add(feeAmount.div(totalSp));
|
||
|
||
//TODO 3. 这部分税会继续拆成两部分,第一部分销毁,第二部分分给所有vDODO持有人
|
||
|
||
|
||
emit Redeem(msg.sender, _amount);
|
||
}
|
||
|
||
//TODO
|
||
function checkReward(uint256 _amount) internal returns(uint256) {
|
||
|
||
// (x - 1)^2 / 81 + (y - 15)^2 / 100 = 1 (5≤ y ≤ 15)
|
||
|
||
// y = 5 (x ≤ 1)
|
||
|
||
// y = 15 (x ≥ 10)
|
||
}
|
||
|
||
// balanceOf = sp-DODO欠款/alpha
|
||
function balanceOf(address _address) public view override returns (uint256 balance) {
|
||
UserInfo memory user = userInfo[_address];
|
||
balance = user.spAmount.sub(user.overdraft.div(alpha));
|
||
}
|
||
|
||
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
|
||
_transfer(_msgSender(), recipient, amount);
|
||
return true;
|
||
}
|
||
|
||
function _transfer(address sender, address recipient, uint256 _amount) internal onlyOperater virtual override {
|
||
require(sender != address(0), " transfer from the zero address");
|
||
require(recipient != address(0), " transfer to the zero address");
|
||
// require(balanceOf(from)≥amount)
|
||
require(balanceOf(sender) >= _amount,"no enough to sp transfer");
|
||
UserInfo storage user = userInfo[sender];
|
||
// sp[msg.sender] -= amount
|
||
user.spAmount = user.spAmount.sub(_amount);
|
||
|
||
// sp[上级[from]] -= amount*0.1 (round down)
|
||
UserInfo storage uplineUser = userInfo[user.upline];
|
||
uplineUser.spAmount = uplineUser.spAmount.sub(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_));
|
||
|
||
UserInfo storage recipientUser = userInfo[recipient];
|
||
// sp[to] += amount
|
||
recipientUser.spAmount = recipientUser.spAmount.add(_amount);
|
||
|
||
UserInfo storage recipientUplineUser = userInfo[recipientUser.upline];
|
||
recipientUplineUser.spAmount =recipientUplineUser.spAmount.add(_amount.mul(_MAG_SP_AMOUNT_).div(_BASE_AMOUNT_));
|
||
emit Transfer(sender, recipient, _amount);
|
||
}
|
||
|
||
// 可提取DODO数额 = sp*alpha - DODO欠款
|
||
function canWithDraw(address _address) public view returns (uint256 withDrawAmount) {
|
||
UserInfo memory user = userInfo[_address];
|
||
withDrawAmount = user.spAmount.mul(alpha).sub(user.overdraft);
|
||
}
|
||
|
||
function checkUserInfo(address _userAddress) public view returns(uint256,address,uint256,uint256,uint256,uint256,bool) {
|
||
UserInfo memory user = userInfo[_userAddress];
|
||
return (user.dodoAmount, user.upline, user.spAmount, user.overdraft,user.lastRewardBlock,user.totalRedeem,user.hasParticipateGov);
|
||
|
||
}
|
||
|
||
|
||
|
||
}
|
||
// deposit 是否需要输入上级地址
|
||
// round up & round down
|
||
//vDODO的分红
|
||
//
|
||
|